From a3212d8ef9f80ba6944d8f29b9ad3650f6c93c49 Mon Sep 17 00:00:00 2001
From: Ulrich Müller ' + _('Hide Search Matches') + ' ' +
+ '' +
+ _("Hide Search Matches") +
+ " The Gentoo policies focus on three aims: Portability. By following the policies, it should be possible
@@ -155,7 +154,7 @@ his goals and reduce the likeliness of surprising behavior. Currently, there are two kinds of tools involved in detecting policy
violations:' + _('Searching') + '
').appendTo(this.out);
- this.dots = $('').appendTo(this.title);
- this.status = $('').appendTo(this.out);
-
- $('#search-progress').text(_('Preparing search...'));
- this.startPulse();
+ const searchText = document.createElement("h2");
+ searchText.textContent = _("Searching");
+ const searchSummary = document.createElement("p");
+ searchSummary.classList.add("search-summary");
+ searchSummary.innerText = "";
+ const searchList = document.createElement("ul");
+ searchList.classList.add("search");
+
+ const out = document.getElementById("search-results");
+ Search.title = out.appendChild(searchText);
+ Search.dots = Search.title.appendChild(document.createElement("span"));
+ Search.status = out.appendChild(searchSummary);
+ Search.output = out.appendChild(searchList);
+
+ const searchProgress = document.getElementById("search-progress");
+ // Some themes don't use the search progress node
+ if (searchProgress) {
+ searchProgress.innerText = _("Preparing search...");
+ }
+ Search.startPulse();
// index already loaded, the browser was quick!
- if (this.hasIndex())
- this.query(query);
- else
- this.deferQuery(query);
+ if (Search.hasIndex()) Search.query(query);
+ else Search.deferQuery(query);
},
/**
* execute search (requires search index to be loaded)
*/
- query : function(query) {
- var i;
-
- // stem the searchterms and add them to the correct list
- var stemmer = new Stemmer();
- var searchterms = [];
- var excluded = [];
- var hlterms = [];
- var tmp = splitQuery(query);
- var objectterms = [];
- for (i = 0; i < tmp.length; i++) {
- if (tmp[i] !== "") {
- objectterms.push(tmp[i].toLowerCase());
- }
+ query: (query) => {
+ const filenames = Search._index.filenames;
+ const docNames = Search._index.docnames;
+ const titles = Search._index.titles;
+ const allTitles = Search._index.alltitles;
+ const indexEntries = Search._index.indexentries;
+
+ // stem the search terms and add them to the correct list
+ const stemmer = new Stemmer();
+ const searchTerms = new Set();
+ const excludedTerms = new Set();
+ const highlightTerms = new Set();
+ const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
+ splitQuery(query.trim()).forEach((queryTerm) => {
+ const queryTermLower = queryTerm.toLowerCase();
+
+ // maybe skip this "word"
+ // stopwords array is from language_data.js
+ if (
+ stopwords.indexOf(queryTermLower) !== -1 ||
+ queryTerm.match(/^\d+$/)
+ )
+ return;
- if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i] === "") {
- // skip this "word"
- continue;
- }
// stem the word
- var word = stemmer.stemWord(tmp[i].toLowerCase());
- var toAppend;
+ let word = stemmer.stemWord(queryTermLower);
// select the correct list
- if (word[0] == '-') {
- toAppend = excluded;
- word = word.substr(1);
- }
+ if (word[0] === "-") excludedTerms.add(word.substr(1));
else {
- toAppend = searchterms;
- hlterms.push(tmp[i].toLowerCase());
+ searchTerms.add(word);
+ highlightTerms.add(queryTermLower);
}
- // only add if not already in the list
- if (!$u.contains(toAppend, word))
- toAppend.push(word);
- }
- var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
+ });
- // console.debug('SEARCH: searching for:');
- // console.info('required: ', searchterms);
- // console.info('excluded: ', excluded);
+ if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js
+ localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" "))
+ }
- // prepare search
- var terms = this._index.terms;
- var titleterms = this._index.titleterms;
+ // console.debug("SEARCH: searching for:");
+ // console.info("required: ", [...searchTerms]);
+ // console.info("excluded: ", [...excludedTerms]);
+
+ // array of [docname, title, anchor, descr, score, filename]
+ let results = [];
+ _removeChildren(document.getElementById("search-progress"));
+
+ const queryLower = query.toLowerCase();
+ for (const [title, foundTitles] of Object.entries(allTitles)) {
+ if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) {
+ for (const [file, id] of foundTitles) {
+ let score = Math.round(100 * queryLower.length / title.length)
+ results.push([
+ docNames[file],
+ titles[file] !== title ? `${titles[file]} > ${title}` : title,
+ id !== null ? "#" + id : "",
+ null,
+ score,
+ filenames[file],
+ ]);
+ }
+ }
+ }
- // array of [filename, title, anchor, descr, score]
- var results = [];
- $('#search-progress').empty();
+ // search for explicit entries in index directives
+ for (const [entry, foundEntries] of Object.entries(indexEntries)) {
+ if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
+ for (const [file, id] of foundEntries) {
+ let score = Math.round(100 * queryLower.length / entry.length)
+ results.push([
+ docNames[file],
+ titles[file],
+ id ? "#" + id : "",
+ null,
+ score,
+ filenames[file],
+ ]);
+ }
+ }
+ }
// lookup as object
- for (i = 0; i < objectterms.length; i++) {
- var others = [].concat(objectterms.slice(0, i),
- objectterms.slice(i+1, objectterms.length));
- results = results.concat(this.performObjectSearch(objectterms[i], others));
- }
+ objectTerms.forEach((term) =>
+ results.push(...Search.performObjectSearch(term, objectTerms))
+ );
// lookup as search terms in fulltext
- results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
+ results.push(...Search.performTermsSearch(searchTerms, excludedTerms));
// let the scorer override scores with a custom scoring function
- if (Scorer.score) {
- for (i = 0; i < results.length; i++)
- results[i][4] = Scorer.score(results[i]);
- }
+ if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item)));
// now sort the results by score (in opposite order of appearance, since the
// display function below uses pop() to retrieve items) and then
// alphabetically
- results.sort(function(a, b) {
- var left = a[4];
- var right = b[4];
- if (left > right) {
- return 1;
- } else if (left < right) {
- return -1;
- } else {
+ results.sort((a, b) => {
+ const leftScore = a[4];
+ const rightScore = b[4];
+ if (leftScore === rightScore) {
// same score: sort alphabetically
- left = a[1].toLowerCase();
- right = b[1].toLowerCase();
- return (left > right) ? -1 : ((left < right) ? 1 : 0);
+ const leftTitle = a[1].toLowerCase();
+ const rightTitle = b[1].toLowerCase();
+ if (leftTitle === rightTitle) return 0;
+ return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
}
+ return leftScore > rightScore ? 1 : -1;
});
+ // remove duplicate search results
+ // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
+ let seen = new Set();
+ results = results.reverse().reduce((acc, result) => {
+ let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
+ if (!seen.has(resultStr)) {
+ acc.push(result);
+ seen.add(resultStr);
+ }
+ return acc;
+ }, []);
+
+ results = results.reverse();
+
// for debugging
//Search.lastresults = results.slice(); // a copy
- //console.info('search results:', Search.lastresults);
+ // console.info("search results:", Search.lastresults);
// print the results
- var resultCount = results.length;
- function displayNextItem() {
- // results left, load the summary and display it
- if (results.length) {
- var item = results.pop();
- var listItem = $('');
- var requestUrl = "";
- var linkUrl = "";
- if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
- // dirhtml builder
- var dirname = item[0] + '/';
- if (dirname.match(/\/index\/$/)) {
- dirname = dirname.substring(0, dirname.length-6);
- } else if (dirname == 'index/') {
- dirname = '';
- }
- requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;
- linkUrl = requestUrl;
-
- } else {
- // normal html builders
- requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;
- linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX;
- }
- listItem.append($('').attr('href',
- linkUrl +
- highlightstring + item[2]).html(item[1]));
- if (item[3]) {
- listItem.append($(' (' + item[3] + ')'));
- Search.output.append(listItem);
- setTimeout(function() {
- displayNextItem();
- }, 5);
- } else if (DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY) {
- $.ajax({url: requestUrl,
- dataType: "text",
- complete: function(jqxhr, textstatus) {
- var data = jqxhr.responseText;
- if (data !== '' && data !== undefined) {
- var summary = Search.makeSearchSummary(data, searchterms, hlterms);
- if (summary) {
- listItem.append(summary);
- }
- }
- Search.output.append(listItem);
- setTimeout(function() {
- displayNextItem();
- }, 5);
- }});
- } else {
- // just display title
- Search.output.append(listItem);
- setTimeout(function() {
- displayNextItem();
- }, 5);
- }
- }
- // search finished, update title and status message
- else {
- Search.stopPulse();
- Search.title.text(_('Search Results'));
- if (!resultCount)
- Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
- else
- Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
- Search.status.fadeIn(500);
- }
- }
- displayNextItem();
+ _displayNextItem(results, results.length, searchTerms);
},
/**
* search for object names
*/
- performObjectSearch : function(object, otherterms) {
- var filenames = this._index.filenames;
- var docnames = this._index.docnames;
- var objects = this._index.objects;
- var objnames = this._index.objnames;
- var titles = this._index.titles;
-
- var i;
- var results = [];
-
- for (var prefix in objects) {
- for (var iMatch = 0; iMatch != objects[prefix].length; ++iMatch) {
- var match = objects[prefix][iMatch];
- var name = match[4];
- var fullname = (prefix ? prefix + '.' : '') + name;
- var fullnameLower = fullname.toLowerCase()
- if (fullnameLower.indexOf(object) > -1) {
- var score = 0;
- var parts = fullnameLower.split('.');
- // check for different match types: exact matches of full name or
- // "last name" (i.e. last dotted part)
- if (fullnameLower == object || parts[parts.length - 1] == object) {
- score += Scorer.objNameMatch;
- // matches in last name
- } else if (parts[parts.length - 1].indexOf(object) > -1) {
- score += Scorer.objPartialMatch;
- }
- var objname = objnames[match[1]][2];
- var title = titles[match[0]];
- // If more than one term searched for, we require other words to be
- // found in the name/title/description
- if (otherterms.length > 0) {
- var haystack = (prefix + ' ' + name + ' ' +
- objname + ' ' + title).toLowerCase();
- var allfound = true;
- for (i = 0; i < otherterms.length; i++) {
- if (haystack.indexOf(otherterms[i]) == -1) {
- allfound = false;
- break;
- }
- }
- if (!allfound) {
- continue;
- }
- }
- var descr = objname + _(', in ') + title;
-
- var anchor = match[3];
- if (anchor === '')
- anchor = fullname;
- else if (anchor == '-')
- anchor = objnames[match[1]][1] + '-' + fullname;
- // add custom score for some objects according to scorer
- if (Scorer.objPrio.hasOwnProperty(match[2])) {
- score += Scorer.objPrio[match[2]];
- } else {
- score += Scorer.objPrioDefault;
- }
- results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]);
- }
+ performObjectSearch: (object, objectTerms) => {
+ const filenames = Search._index.filenames;
+ const docNames = Search._index.docnames;
+ const objects = Search._index.objects;
+ const objNames = Search._index.objnames;
+ const titles = Search._index.titles;
+
+ const results = [];
+
+ const objectSearchCallback = (prefix, match) => {
+ const name = match[4]
+ const fullname = (prefix ? prefix + "." : "") + name;
+ const fullnameLower = fullname.toLowerCase();
+ if (fullnameLower.indexOf(object) < 0) return;
+
+ let score = 0;
+ const parts = fullnameLower.split(".");
+
+ // check for different match types: exact matches of full name or
+ // "last name" (i.e. last dotted part)
+ if (fullnameLower === object || parts.slice(-1)[0] === object)
+ score += Scorer.objNameMatch;
+ else if (parts.slice(-1)[0].indexOf(object) > -1)
+ score += Scorer.objPartialMatch; // matches in last name
+
+ const objName = objNames[match[1]][2];
+ const title = titles[match[0]];
+
+ // If more than one term searched for, we require other words to be
+ // found in the name/title/description
+ const otherTerms = new Set(objectTerms);
+ otherTerms.delete(object);
+ if (otherTerms.size > 0) {
+ const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
+ if (
+ [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
+ )
+ return;
}
- }
+ let anchor = match[3];
+ if (anchor === "") anchor = fullname;
+ else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
+
+ const descr = objName + _(", in ") + title;
+
+ // add custom score for some objects according to scorer
+ if (Scorer.objPrio.hasOwnProperty(match[2]))
+ score += Scorer.objPrio[match[2]];
+ else score += Scorer.objPrioDefault;
+
+ results.push([
+ docNames[match[0]],
+ fullname,
+ "#" + anchor,
+ descr,
+ score,
+ filenames[match[0]],
+ ]);
+ };
+ Object.keys(objects).forEach((prefix) =>
+ objects[prefix].forEach((array) =>
+ objectSearchCallback(prefix, array)
+ )
+ );
return results;
},
- /**
- * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
- */
- escapeRegExp : function(string) {
- return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
- },
-
/**
* search for full-text terms in the index
*/
- performTermsSearch : function(searchterms, excluded, terms, titleterms) {
- var docnames = this._index.docnames;
- var filenames = this._index.filenames;
- var titles = this._index.titles;
+ performTermsSearch: (searchTerms, excludedTerms) => {
+ // prepare search
+ const terms = Search._index.terms;
+ const titleTerms = Search._index.titleterms;
+ const filenames = Search._index.filenames;
+ const docNames = Search._index.docnames;
+ const titles = Search._index.titles;
- var i, j, file;
- var fileMap = {};
- var scoreMap = {};
- var results = [];
+ const scoreMap = new Map();
+ const fileMap = new Map();
// perform the search on the required terms
- for (i = 0; i < searchterms.length; i++) {
- var word = searchterms[i];
- var files = [];
- var _o = [
- {files: terms[word], score: Scorer.term},
- {files: titleterms[word], score: Scorer.title}
+ searchTerms.forEach((word) => {
+ const files = [];
+ const arr = [
+ { files: terms[word], score: Scorer.term },
+ { files: titleTerms[word], score: Scorer.title },
];
// add support for partial matches
if (word.length > 2) {
- var word_regex = this.escapeRegExp(word);
- for (var w in terms) {
- if (w.match(word_regex) && !terms[word]) {
- _o.push({files: terms[w], score: Scorer.partialTerm})
- }
- }
- for (var w in titleterms) {
- if (w.match(word_regex) && !titleterms[word]) {
- _o.push({files: titleterms[w], score: Scorer.partialTitle})
- }
- }
+ const escapedWord = _escapeRegExp(word);
+ Object.keys(terms).forEach((term) => {
+ if (term.match(escapedWord) && !terms[word])
+ arr.push({ files: terms[term], score: Scorer.partialTerm });
+ });
+ Object.keys(titleTerms).forEach((term) => {
+ if (term.match(escapedWord) && !titleTerms[word])
+ arr.push({ files: titleTerms[word], score: Scorer.partialTitle });
+ });
}
// no match but word was a required one
- if ($u.every(_o, function(o){return o.files === undefined;})) {
- break;
- }
+ if (arr.every((record) => record.files === undefined)) return;
+
// found search word in contents
- $u.each(_o, function(o) {
- var _files = o.files;
- if (_files === undefined)
- return
-
- if (_files.length === undefined)
- _files = [_files];
- files = files.concat(_files);
-
- // set score for the word in each file to Scorer.term
- for (j = 0; j < _files.length; j++) {
- file = _files[j];
- if (!(file in scoreMap))
- scoreMap[file] = {};
- scoreMap[file][word] = o.score;
- }
+ arr.forEach((record) => {
+ if (record.files === undefined) return;
+
+ let recordFiles = record.files;
+ if (recordFiles.length === undefined) recordFiles = [recordFiles];
+ files.push(...recordFiles);
+
+ // set score for the word in each file
+ recordFiles.forEach((file) => {
+ if (!scoreMap.has(file)) scoreMap.set(file, {});
+ scoreMap.get(file)[word] = record.score;
+ });
});
// create the mapping
- for (j = 0; j < files.length; j++) {
- file = files[j];
- if (file in fileMap && fileMap[file].indexOf(word) === -1)
- fileMap[file].push(word);
- else
- fileMap[file] = [word];
- }
- }
+ files.forEach((file) => {
+ if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1)
+ fileMap.get(file).push(word);
+ else fileMap.set(file, [word]);
+ });
+ });
// now check if the files don't contain excluded terms
- for (file in fileMap) {
- var valid = true;
-
+ const results = [];
+ for (const [file, wordList] of fileMap) {
// check if all requirements are matched
- var filteredTermCount = // as search terms with length < 3 are discarded: ignore
- searchterms.filter(function(term){return term.length > 2}).length
+
+ // as search terms with length < 3 are discarded
+ const filteredTermCount = [...searchTerms].filter(
+ (term) => term.length > 2
+ ).length;
if (
- fileMap[file].length != searchterms.length &&
- fileMap[file].length != filteredTermCount
- ) continue;
+ wordList.length !== searchTerms.size &&
+ wordList.length !== filteredTermCount
+ )
+ continue;
// ensure that none of the excluded terms is in the search result
- for (i = 0; i < excluded.length; i++) {
- if (terms[excluded[i]] == file ||
- titleterms[excluded[i]] == file ||
- $u.contains(terms[excluded[i]] || [], file) ||
- $u.contains(titleterms[excluded[i]] || [], file)) {
- valid = false;
- break;
- }
- }
+ if (
+ [...excludedTerms].some(
+ (term) =>
+ terms[term] === file ||
+ titleTerms[term] === file ||
+ (terms[term] || []).includes(file) ||
+ (titleTerms[term] || []).includes(file)
+ )
+ )
+ break;
- // if we have still a valid result we can add it to the result list
- if (valid) {
- // select one (max) score for the file.
- // for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
- var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
- results.push([docnames[file], titles[file], '', null, score, filenames[file]]);
- }
+ // select one (max) score for the file.
+ const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w]));
+ // add result to the result list
+ results.push([
+ docNames[file],
+ titles[file],
+ "",
+ null,
+ score,
+ filenames[file],
+ ]);
}
return results;
},
@@ -492,34 +539,28 @@ var Search = {
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
- * of stemmed words, hlwords is the list of normal, unstemmed
- * words. the first one is used to find the occurrence, the
- * latter for highlighting it.
+ * of stemmed words.
*/
- makeSearchSummary : function(htmlText, keywords, hlwords) {
- var text = Search.htmlToText(htmlText);
- if (text == "") {
- return null;
- }
- var textLower = text.toLowerCase();
- var start = 0;
- $.each(keywords, function() {
- var i = textLower.indexOf(this.toLowerCase());
- if (i > -1)
- start = i;
- });
- start = Math.max(start - 120, 0);
- var excerpt = ((start > 0) ? '...' : '') +
- $.trim(text.substr(start, 240)) +
- ((start + 240 - text.length) ? '...' : '');
- var rv = $('').text(excerpt);
- $.each(hlwords, function() {
- rv = rv.highlightText(this, 'highlighted');
- });
- return rv;
- }
+ makeSearchSummary: (htmlText, keywords) => {
+ const text = Search.htmlToText(htmlText);
+ if (text === "") return null;
+
+ const textLower = text.toLowerCase();
+ const actualStartPosition = [...keywords]
+ .map((k) => textLower.indexOf(k.toLowerCase()))
+ .filter((i) => i > -1)
+ .slice(-1)[0];
+ const startWithContext = Math.max(actualStartPosition - 120, 0);
+
+ const top = startWithContext === 0 ? "" : "...";
+ const tail = startWithContext + 240 < text.length ? "..." : "";
+
+ let summary = document.createElement("p");
+ summary.classList.add("context");
+ summary.textContent = top + text.substr(startWithContext, 240).trim() + tail;
+
+ return summary;
+ },
};
-$(document).ready(function() {
- Search.init();
-});
+_ready(Search.init);
diff --git a/_static/sphinx_highlight.js b/_static/sphinx_highlight.js
new file mode 100644
index 0000000..aae669d
--- /dev/null
+++ b/_static/sphinx_highlight.js
@@ -0,0 +1,144 @@
+/* Highlighting utilities for Sphinx HTML documentation. */
+"use strict";
+
+const SPHINX_HIGHLIGHT_ENABLED = true
+
+/**
+ * highlight a given string on a node by wrapping it in
+ * span elements with the given class name.
+ */
+const _highlight = (node, addItems, text, className) => {
+ if (node.nodeType === Node.TEXT_NODE) {
+ const val = node.nodeValue;
+ const parent = node.parentNode;
+ const pos = val.toLowerCase().indexOf(text);
+ if (
+ pos >= 0 &&
+ !parent.classList.contains(className) &&
+ !parent.classList.contains("nohighlight")
+ ) {
+ let span;
+
+ const closestNode = parent.closest("body, svg, foreignObject");
+ const isInSVG = closestNode && closestNode.matches("svg");
+ if (isInSVG) {
+ span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
+ } else {
+ span = document.createElement("span");
+ span.classList.add(className);
+ }
+
+ span.appendChild(document.createTextNode(val.substr(pos, text.length)));
+ parent.insertBefore(
+ span,
+ parent.insertBefore(
+ document.createTextNode(val.substr(pos + text.length)),
+ node.nextSibling
+ )
+ );
+ node.nodeValue = val.substr(0, pos);
+
+ if (isInSVG) {
+ const rect = document.createElementNS(
+ "http://www.w3.org/2000/svg",
+ "rect"
+ );
+ const bbox = parent.getBBox();
+ rect.x.baseVal.value = bbox.x;
+ rect.y.baseVal.value = bbox.y;
+ rect.width.baseVal.value = bbox.width;
+ rect.height.baseVal.value = bbox.height;
+ rect.setAttribute("class", className);
+ addItems.push({ parent: parent, target: rect });
+ }
+ }
+ } else if (node.matches && !node.matches("button, select, textarea")) {
+ node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
+ }
+};
+const _highlightText = (thisNode, text, className) => {
+ let addItems = [];
+ _highlight(thisNode, addItems, text, className);
+ addItems.forEach((obj) =>
+ obj.parent.insertAdjacentElement("beforebegin", obj.target)
+ );
+};
+
+/**
+ * Small JavaScript module for the documentation.
+ */
+const SphinxHighlight = {
+
+ /**
+ * highlight the search words provided in localstorage in the text
+ */
+ highlightSearchWords: () => {
+ if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
+
+ // get and clear terms from localstorage
+ const url = new URL(window.location);
+ const highlight =
+ localStorage.getItem("sphinx_highlight_terms")
+ || url.searchParams.get("highlight")
+ || "";
+ localStorage.removeItem("sphinx_highlight_terms")
+ url.searchParams.delete("highlight");
+ window.history.replaceState({}, "", url);
+
+ // get individual terms from highlight string
+ const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
+ if (terms.length === 0) return; // nothing to do
+
+ // There should never be more than one element matching "div.body"
+ const divBody = document.querySelectorAll("div.body");
+ const body = divBody.length ? divBody[0] : document.querySelector("body");
+ window.setTimeout(() => {
+ terms.forEach((term) => _highlightText(body, term, "highlighted"));
+ }, 10);
+
+ const searchBox = document.getElementById("searchbox");
+ if (searchBox === null) return;
+ searchBox.appendChild(
+ document
+ .createRange()
+ .createContextualFragment(
+ '
Basic information¶
+Basic information¶
Goals of policy making¶
+Goals of policy making¶
Policy compliance checking¶
+Policy compliance checking¶
@@ -178,7 +177,7 @@ supplements the direct use of linting tools by developers with reliable
tree-wide scans.
The Gentoo QA team is tasked with enforcement of the tree policies. Its role is governed by GLEP 48. It focuses on documenting the policies, resolving doubts regarding them and educating @@ -191,7 +190,7 @@ refuses to resolve policy violations.
team can issue disciplinary measures against the developer in question.The majority of policies are written down and maintained by the QA
team. Other Gentoo projects also create policies that affect their
specific areas of contributions (e.g. systemd project created
@@ -285,7 +284,7 @@ be appealed to the Gentoo Policy Guide
Gentoo Policy Guide aims to become a definitive clear source of all
Tree Policies that are currently binding to Gentoo developers.
It combines both policies that are global by design (i.e. set by the QA
@@ -121,9 +120,9 @@ indication of the body setting the policy, and therefore the process
in which the policy can be updated. Gentoo Policy Guide aims to become a definitive clear source of all
Tree Policies that are currently binding to Gentoo developers.
It combines both policies that are global by design (i.e. set by the QA
@@ -133,23 +132,24 @@ indication of the body setting the policy, and therefore the process
in which the policy can be updated. This work is licensed under the Creative Commons Attribution-ShareAlike
4.0 International License. At the time, Gentoo was lacking a clear and focused document listing all
development-related policies in a concise and clear way. PMS provided a technical specification for the ebuild files but did
@@ -176,7 +176,7 @@ established less or more formally in the past but were never really
written down. The Policy Guide was created in order to address aforementioned
documentation deficiencies. Its primary purpose is to collect all
applicable policies from various sources and combine them into a single
@@ -193,9 +193,9 @@ devmanual) should conform to policies stated here. The Gentoo policies focus on three aims: Portability. By following the policies, it should be possible
@@ -214,7 +214,7 @@ his goals and reduce the likeliness of surprising behavior. Currently, there are two kinds of tools involved in detecting policy
violations: The Gentoo QA team is tasked with enforcement of the tree policies.
Its role is governed by GLEP 48. It focuses on documenting
the policies, resolving doubts regarding them and educating
@@ -250,7 +250,7 @@ refuses to resolve policy violations. The majority of policies are written down and maintained by the QA
team. Other Gentoo projects also create policies that affect their
specific areas of contributions (e.g. systemd project created
@@ -263,11 +263,11 @@ be appealed to the PMS provides the specification of ebuild format, as well as general
guidelines for implementing package managers. All ebuilds in the Gentoo
repository are required to conform to the PMS. Tree policies may
@@ -277,7 +277,7 @@ in subsequent EAPIs that are approved by the Council. The project’s
wiki page discusses how PMS can be changed via future EAPI process. GLEPs provide the highest level policies applicable to Gentoo. Final
or active GLEPs apply to all developers. Tree policies may impose
additional restrictions on GLEPs but may not override them. Devmanual is the basic guide for ebuild developers. Besides policies,
it contains many general recommendations and detailed instructions.
Developer Manual does not specify policies itself, and needs to comply
@@ -296,16 +296,16 @@ recommended that all changes are reviewed by the
POSIX is the basic standard for operating systems. However, its rules
apply to the software packaged in Gentoo rather than the distribution
itself. Nevertheless, when no more specific policy applies, following
POSIX is recommended. FHS specifies the suggested filesystem layout for Linux systems.
Gentoo follows FHS only partially. Whenever Gentoo policies and FHS
disagree, Gentoo policies should be followed. 0001 QA no 0002 QA by repoman and pkgcheck 0011 QA https://archives.gentoo.org/gentoo-portage-dev/message/9cae3a92412a007febe7ac0612d50f5f by repoman and pkgcheck
+ © 2001–2023 Gentoo Authors, Inc.
Gentoo is a trademark of the Gentoo Foundation, Inc.
The contents of this document, unless otherwise expressly stated, are licensed under the
diff --git a/combined.html b/combined.html
index 5134e1d..f4c4d87 100644
--- a/combined.html
+++ b/combined.html
@@ -1,11 +1,11 @@
-
+
-
+
Gentoo Policy Guide¶
+Gentoo Policy Guide¶
Preface¶
+Preface¶
Introduction¶
+Introduction¶
License¶
+License¶
Motivation and history¶
+Motivation and history¶
Historical state of policy documentation¶
+Historical state of policy documentation¶
Purpose of the Policy Guide¶
+Purpose of the Policy Guide¶
Basic information¶
+Basic information¶
Goals of policy making¶
+Goals of policy making¶
Policy compliance checking¶
+Policy compliance checking¶
@@ -237,7 +237,7 @@ supplements the direct use of linting tools by developers with reliable
tree-wide scans.
Policy enforcement¶
+Policy enforcement¶
Policy making, changes and appeals¶
+Policy making, changes and appeals¶
Other policy documents¶
+Other policy documents¶
Gentoo-specific documentation¶
+Gentoo-specific documentation¶
Package Manager Specification¶
+Package Manager Specification¶
GLEPs¶
+GLEPs¶
Developer Manual¶
+Developer Manual¶
External standards¶
+External standards¶
POSIX¶
+POSIX¶
FHS¶
+FHS¶
Dependencies¶
+Dependencies¶
Optional runtime dependencies¶
+Optional runtime dependencies¶
-
@@ -349,15 +349,15 @@ by the Council but no reference implementation has been written.
=-dependencies with no revision¶
+=-dependencies with no revision¶
-
@@ -381,20 +381,20 @@ requesting the latter, it warns developers to reconsider whether they
used the correct operator.
Slot and subslot dependencies¶
+Slot and subslot dependencies¶
on (sub-)slotted packages¶
+on (sub-)slotted packages¶
-
@@ -420,18 +420,18 @@ to the newest package version available.
0012
Qt project
https://wiki.gentoo.org/wiki/Project:Qt/Policies#Dependencies
no
-proactive use of slot operators¶
+proactive use of slot operators¶
There is an open debate on whether developers should be proactively
adding :=
slot operators on packages that do not define subslots
yet.
@@ -456,18 +456,18 @@ They point out the case of Qt packages as an example.
0003
Council
https://projects.gentoo.org/council/meeting-logs/20151011-summary.txt
no
0021
QA (inferred from PMS)
by pkgcheck
1001
Council
https://gitweb.gentoo.org/repo/gentoo.git/tree/metadata/layout.conf
by pkgcheck and repoman
metadata/layout.conf
.
1003
individual eclass maintainers
by pkgcheck and repoman
0101
QA
partially via repoman and pkgcheck
0102
QA
no
0103
QA
by pkgcheck, highlighted as error by gentoo-syntax
0104
QA
by pkgcheck
0105
QA
no
0106
QA
no
0107
+QA
+no
+The D
and ED
variables must be used only in the src_install
+and pkg_preinst
phase functions. Exceptions to this policy can be
+granted by the QA team.
Rationale: using D
in other phases (e.g. src_configure
) is
+error-prone and may lead to the path being embedded in files of the
+installed image. In addition, the directory pointed to by ${D}
+does not exist in other phases.
0201
QA
https://gitweb.gentoo.org/repo/gentoo.git/tree/metadata/install-qa-check.d/08gentoo-paths
via install-qa-check.d
0202
QA
https://projects.gentoo.org/council/meeting-logs/20130813-summary.txt https://projects.gentoo.org/council/meeting-logs/20130924-summary.txt
no
0203
QA
https://gitweb.gentoo.org/proj/portage.git/tree/bin/install-qa-check.d/80multilib-strict
via install-qa-check.d, fatal
0204
QA
https://gitweb.gentoo.org/proj/portage.git/tree/bin/install-qa-check.d/80libraries
via install-qa-check.d, fatal
0205
Council, clarified by QA
https://projects.gentoo.org/council/meeting-logs/20151213-summary.txt https://projects.gentoo.org/council/meeting-logs/20151011-summary.txt
via install-qa-check.d
0206
QA
by repoman and pkgcheck (when ebuild-generated)
0301
QA
no
0302
QA
no
0303
QA
no
It is recommended to use the following one-liner to remove .la files:
-find "${ED}" -name '*.la' -delete || die
+find "${ED}" -type f -name '*.la' -delete || die
Rationale: libtool files were historically introduced as an attempt
@@ -1044,15 +1065,15 @@ and while libtool keeps generating them, they are considered
unnecessary and potentially harmful.
-Virtuals¶
+Virtuals¶
-- PG
+- PG:
0304
-- Source
+- Source:
QA
-- Reported
+- Reported:
no
@@ -1065,17 +1086,17 @@ be impossible if they installed any files).
-Keywording and stabilization¶
+Keywording and stabilization¶
-Rekeywording on dropped keywords¶
+Rekeywording on dropped keywords¶
-- PG
+- PG:
0401
-- Source
+- Source:
QA
-- Reported
+- Reported:
by pkgcheck and repoman
@@ -1089,15 +1110,15 @@ affects other developers who in the future either want to stabilize
a new version or to remove an old version.
-Stabilizing new versions¶
+Stabilizing new versions¶
-- PG
+- PG:
0402
-- Source
+- Source:
QA
-- Reported
+- Reported:
by pkgcheck
@@ -1119,18 +1140,18 @@ is stable and whether arch teams are slacking or stabilization was not
requested on remaining architectures in the first place.
-Removing stable keywords¶
+Removing stable keywords¶
-- PG
+- PG:
0403
-- Source
+- Source:
QA
-- Reference
+- Reference:
-
-- Reported
+- Reported:
n/a
@@ -1144,22 +1165,22 @@ of last rite mails.
-Language-specific policies¶
+Language-specific policies¶
-Python¶
+Python¶
-Eclass usage¶
+Eclass usage¶
-- PG
+- PG:
0501
-- Source
+- Source:
Python project
-- Reference
+- Reference:
-
-- Reported
+- Reported:
by pkgcheck
@@ -1181,18 +1202,18 @@ and variables also make it possible to gracefully retire old
implementations with minimal changes to existing ebuilds.
-Python 2 deprecation¶
+Python 2 deprecation¶
-- PG
+- PG:
0502
-- Source
+- Source:
Python project
-- Reference
+- Reference:
https://wiki.gentoo.org/wiki/Project:Python#Python_2_end-of-life
-- Reported
+- Reported:
no
@@ -1217,17 +1238,17 @@ will cause major upgrade issues.
-Package Maintainers¶
+Package Maintainers¶
-Adding new maintainers¶
+Adding new maintainers¶
-- PG
+- PG:
0601
-- Source
+- Source:
QA
-- Reported
+- Reported:
no
@@ -1248,15 +1269,15 @@ packages ended up being maintained solely by Python, and distinguishing
them from packages actually within project’s profile was hard.
-New packages without a maintainer¶
+New packages without a maintainer¶
-- PG
+- PG:
0602
-- Source
+- Source:
QA
-- Reported
+- Reported:
no
@@ -1270,15 +1291,15 @@ to increase their maintenance burden by adding new packages and refusing
to take care of them.
-Removing package maintainers¶
+Removing package maintainers¶
-- PG
+- PG:
0603
-- Source
+- Source:
QA
-- Reported
+- Reported:
no
@@ -1302,20 +1323,20 @@ to them silently becoming maintainer-needed).
-Other metadata variables¶
+Other metadata variables¶
-Dynamic slots (multislot flag)¶
+Dynamic slots (multislot flag)¶
-- PG
+- PG:
0701
-- Source
+- Source:
QA (inferred from PMS)
-- Reference
+- Reference:
https://wiki.gentoo.org/index.php?title=Project:Quality_Assurance/Policies&oldid=109991#multislot.2FUSE-dependent_SLOT, https://bugs.gentoo.org/174407
-- Reported
+- Reported:
use
in global scope triggers fatal error
@@ -1342,18 +1363,18 @@ manager behavior which could include use of unpredictable slot, cache
invalidation or explicit errors.
-HOMEPAGE value must be meaningful¶
+HOMEPAGE value must be meaningful¶
-- PG
+- PG:
0702
-- Source
+- Source:
QA
-- Reference
+- Reference:
https://archives.gentoo.org/gentoo-dev/message/83cc5bbd7bbe8bdf04dd3c3bc7f8a035
-- Reported
+- Reported:
known bad values are reported by pkgcheck
@@ -1373,15 +1394,15 @@ those cases, using the explicit No_homepage marker at least makes it
easy to identify such packages.
-RESTRICT=test for USE=-test¶
+RESTRICT=test for USE=-test¶
-- PG
+- PG:
0703
-- Source
+- Source:
QA
-- Reported
+- Reported:
by pkgcheck
@@ -1407,29 +1428,31 @@ common, and there is little harm in overspecifying it.
0704
QA
no
The LICENSE
variable must explicitly list licenses for all files
-installed by the package. If some of the applicable licenses are
-conditional to USE flags, appropriate USE conditionals need to
-be expressed in the variable.
The LICENSE
variable must explicitly list all licenses pertaining
+to the “corresponding source” of the files installed by the package.
+This includes all their source code, but also all scripts used to
+control compilation and installation. If some of the applicable
+licenses are conditional to USE flags, appropriate USE conditionals
+need to be expressed in the variable.
If a package bundles any dependencies that are either installed, statically linked or in any other way combined with installed files, the licenses of these dependencies need to be listed as well. This is not presently required when statically linking to dependencies installed by separate packages in the repository.
-The licenses for files that are not installed but that are used at build -time are not listed explicitly.
+The licenses for files that are neither installed nor used at build +time shall not be listed.
Rationale: the primary purpose of the license support in the package manager is to provide the users with ability to decide on acceptable licenses for their installed systems (and binary packages). In order @@ -1445,10 +1468,6 @@ static linking to other packages, as in the latter case it is non-trivial to implement and the package manager already verifies the license while building dependencies (but not when installing binary packages).
-The ebuild format does not provide a separate variable to list licenses -needed only at build time. So far it has not been considered important -enough to have one, as the relevant files exist only temporarily -on the user’s system and do not affect the runtime use of packages.
Note
Please remember to include the licenses of support files provided @@ -1457,20 +1476,20 @@ by the ebuild, e.g. init.d scripts (usually GPL-2).
0801
QA
no
0802
QA
https://archives.gentoo.org/gentoo-dev/message/cf3f5a59ac918335766632bd02438722
no
0803
Council
https://projects.gentoo.org/council/meeting-logs/20191013-summary.txt
by pkgcheck
0901
QA
by repoman and pkgcheck (as deprecated eclass)
0012
Qt project
https://wiki.gentoo.org/wiki/Project:Qt/Policies#Dependencies
no
-proactive use of slot operators¶
+proactive use of slot operators¶
There is an open debate on whether developers should be proactively
adding :=
slot operators on packages that do not define subslots
yet.
@@ -277,18 +276,18 @@ They point out the case of Qt packages as an example.
0003
Council
https://projects.gentoo.org/council/meeting-logs/20151011-summary.txt
no
0021
QA (inferred from PMS)
by pkgcheck
1001
Council
https://gitweb.gentoo.org/repo/gentoo.git/tree/metadata/layout.conf
by pkgcheck and repoman
metadata/layout.conf
.
1003
individual eclass maintainers
by pkgcheck and repoman
0101
QA
partially via repoman and pkgcheck
0102
QA
no
0103
QA
by pkgcheck, highlighted as error by gentoo-syntax
0104
QA
by pkgcheck
0105
QA
no
0106
QA
no
0107
+QA
+no
+The D
and ED
variables must be used only in the src_install
+and pkg_preinst
phase functions. Exceptions to this policy can be
+granted by the QA team.
Rationale: using D
in other phases (e.g. src_configure
) is
+error-prone and may lead to the path being embedded in files of the
+installed image. In addition, the directory pointed to by ${D}
+does not exist in other phases.
0201
QA
https://gitweb.gentoo.org/repo/gentoo.git/tree/metadata/install-qa-check.d/08gentoo-paths
via install-qa-check.d
0202
QA
https://projects.gentoo.org/council/meeting-logs/20130813-summary.txt https://projects.gentoo.org/council/meeting-logs/20130924-summary.txt
no
0203
QA
https://gitweb.gentoo.org/proj/portage.git/tree/bin/install-qa-check.d/80multilib-strict
via install-qa-check.d, fatal
0204
QA
https://gitweb.gentoo.org/proj/portage.git/tree/bin/install-qa-check.d/80libraries
via install-qa-check.d, fatal
0205
Council, clarified by QA
https://projects.gentoo.org/council/meeting-logs/20151213-summary.txt https://projects.gentoo.org/council/meeting-logs/20151011-summary.txt
via install-qa-check.d
0206
QA
by repoman and pkgcheck (when ebuild-generated)
| ||
+ |
+ PG0107 (D must be used only in src_install and pkg_preinst) | + |
File system layout |