They work fine with Python 2.7 and Python 3.5 anyway and some systems do not
have a "python2" binary (e.g. macOS). This will fix the Gerrit CI build on
Bazel's CI, which is currently failing with:
ERROR: /Users/buildkite/builds/buildkite-macpro-7-1/bazel-downstream-projects/gerrit/Documentation/BUILD:94:1: Couldn't build file Documentation/pg-plugin-endpoints.txt.html_htmlonly_expn: error executing shell command: '.html_htmlonly_expn' failed (Exit 127)
env: python2: No such file or directory
I tested this change like this:
# Build with Python 2 to ensure my local setup is fine.
bazel build //Documentation/...
# Works fine.
# Now build with Python 3 instead, to ensure compatibility.
sed -i 's/python2/python3/g' Documentation/replace_macros.py \
    tools/bzl/license.bzl
bazel clean
bazel build //Documentation/...
# Works fine, too.
Thus we can safely change "python2" to "python" and let the scripts run with
the system default version.
Change-Id: I107b76bc71e9b42101d57f94ab7c54c90e7c74cb
		
	
		
			
				
	
	
		
			283 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			283 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env python
 | 
						|
# coding=utf-8
 | 
						|
# Copyright (C) 2013 The Android Open Source Project
 | 
						|
#
 | 
						|
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
# you may not use this file except in compliance with the License.
 | 
						|
# You may obtain a copy of the License at
 | 
						|
#
 | 
						|
# http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
#
 | 
						|
# Unless required by applicable law or agreed to in writing, software
 | 
						|
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
# See the License for the specific language governing permissions and
 | 
						|
# limitations under the License.
 | 
						|
 | 
						|
from optparse import OptionParser
 | 
						|
import re
 | 
						|
import sys
 | 
						|
 | 
						|
PAT_GERRIT = re.compile(r'^GERRIT')
 | 
						|
PAT_INCLUDE = re.compile(r'^(include::.*)(\[\])$')
 | 
						|
PAT_GET = re.compile(r'^get::([^ \t\n]*)')
 | 
						|
PAT_TITLE = re.compile(r'^\.(.*)')
 | 
						|
PAT_STARS = re.compile(r'^\*\*\*\*')
 | 
						|
PAT_SEARCHBOX = re.compile(r'^SEARCHBOX')
 | 
						|
 | 
						|
GERRIT_UPLINK = """
 | 
						|
 | 
						|
++++
 | 
						|
<hr style=\"
 | 
						|
  height: 2px;
 | 
						|
  color: silver;
 | 
						|
  margin-top: 1.2em;
 | 
						|
  margin-bottom: 0.5em;
 | 
						|
\">
 | 
						|
++++
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
GET_TITLE = '<div class="title">%s</div>'
 | 
						|
 | 
						|
GET_MACRO = """
 | 
						|
 | 
						|
++++
 | 
						|
<div class="listingblock">
 | 
						|
%s
 | 
						|
<div class="content">
 | 
						|
<a id=\"{0}\" onmousedown="javascript:
 | 
						|
  var i = document.URL.lastIndexOf(\'/Documentation/\');
 | 
						|
  var url = document.URL.substring(0, i) + \'{0}\';
 | 
						|
  document.getElementById(\'{0}\').href = url;">
 | 
						|
    GET {0} HTTP/1.0
 | 
						|
</a>
 | 
						|
</div>
 | 
						|
</div>
 | 
						|
++++
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
SEARCH_BOX = """
 | 
						|
 | 
						|
++++
 | 
						|
<div style="
 | 
						|
  position:fixed;
 | 
						|
  top:0px;
 | 
						|
  right:0px;
 | 
						|
  text-align:
 | 
						|
  right;
 | 
						|
  padding-top:2px;
 | 
						|
  padding-right:0.5em;
 | 
						|
  padding-bottom:2px;">
 | 
						|
<input size="40"
 | 
						|
  style="line-height: 0.75em;font-size: 0.75em;"
 | 
						|
  id="docSearch"
 | 
						|
  type="text">
 | 
						|
<button style="
 | 
						|
  background:none!important;
 | 
						|
  border:none;
 | 
						|
  padding:0!important;
 | 
						|
  vertical-align:bottom;
 | 
						|
  font-family:'Open Sans','DejaVu Sans',sans-serif;
 | 
						|
  font-size:0.8em;
 | 
						|
  color:#1d4b8f;
 | 
						|
  text-decoration:none;"
 | 
						|
  type="button"
 | 
						|
  id="searchBox">
 | 
						|
  Search
 | 
						|
</button>
 | 
						|
<script type="text/javascript">
 | 
						|
var f = function() {
 | 
						|
  window.location = '../#/Documentation/' +
 | 
						|
    encodeURIComponent(document.getElementById("docSearch").value);
 | 
						|
}
 | 
						|
document.getElementById("searchBox").onclick = f;
 | 
						|
document.getElementById("docSearch").onkeypress = function(e) {
 | 
						|
  if (13 == (e.keyCode ? e.keyCode : e.which)) {
 | 
						|
    f();
 | 
						|
  }
 | 
						|
}
 | 
						|
</script>
 | 
						|
</div>
 | 
						|
++++
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
LINK_SCRIPT = """
 | 
						|
 | 
						|
++++
 | 
						|
<script type="text/javascript">
 | 
						|
    decorate(document.getElementsByTagName('h1'));
 | 
						|
    decorate(document.getElementsByTagName('h2'));
 | 
						|
    decorate(document.getElementsByTagName('h3'));
 | 
						|
    decorate(document.getElementsByTagName('h4'));
 | 
						|
 | 
						|
    var divs = document.getElementsByTagName('div');
 | 
						|
    var arr = new Array();
 | 
						|
    var excluded = getExcludedIds();
 | 
						|
    for(var i = 0; i < divs.length; i++) {
 | 
						|
      var d = divs[i];
 | 
						|
      var id = d.getAttribute('id');
 | 
						|
      if (id != null && !(id in excluded)) {
 | 
						|
        arr[arr.length] = d;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    decorate(arr);
 | 
						|
 | 
						|
    var anchors = document.getElementsByTagName('a');
 | 
						|
    arr = new Array();
 | 
						|
    for(var i = 0; i < anchors.length; i++) {
 | 
						|
      var a = anchors[i];
 | 
						|
      // if the anchor has no id there is no target to
 | 
						|
      // which we can link
 | 
						|
      if (a.getAttribute('id') != null) {
 | 
						|
        // if the anchor is empty there is no content which
 | 
						|
        // can receive the mouseover event, an empty anchor
 | 
						|
        // applies to the element that follows, move the
 | 
						|
        // element that follows into the anchor so that there
 | 
						|
        // is content which can receive the mouseover event
 | 
						|
        if (a.firstChild == null) {
 | 
						|
          var next = a.nextSibling;
 | 
						|
          if (next != null) {
 | 
						|
            next.parentNode.removeChild(next);
 | 
						|
            a.appendChild(next);
 | 
						|
          }
 | 
						|
        }
 | 
						|
        arr[arr.length] = a;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    decorate(arr);
 | 
						|
 | 
						|
    function decorate(e) {
 | 
						|
      for(var i = 0; i < e.length; i++) {
 | 
						|
        e[i].onmouseover = function (evt) {
 | 
						|
          var element = this;
 | 
						|
          // do nothing if the link icon is currently showing
 | 
						|
          var a = element.firstChild;
 | 
						|
          if (a != null && a instanceof Element
 | 
						|
              && a.getAttribute('id') == 'LINK') {
 | 
						|
            return;
 | 
						|
          }
 | 
						|
 | 
						|
          // if there is no id there is no target to link to
 | 
						|
          var id = element.getAttribute('id');
 | 
						|
          if (id == null) {
 | 
						|
            return;
 | 
						|
          }
 | 
						|
 | 
						|
          // create and show a link icon that links to this element
 | 
						|
          a = document.createElement('a');
 | 
						|
          a.setAttribute('id', 'LINK');
 | 
						|
          a.setAttribute('href', '#' + id);
 | 
						|
          a.setAttribute('style', 'position: absolute;'
 | 
						|
              + ' left: ' + (element.offsetLeft - 16 - 2 * 4) + 'px;'
 | 
						|
              + ' padding-left: 4px; padding-right: 4px;');
 | 
						|
          var span = document.createElement('span');
 | 
						|
          span.setAttribute('style', 'height: ' + element.offsetHeight + 'px;'
 | 
						|
              + ' display: inline-block; vertical-align: baseline;'
 | 
						|
              + ' font-size: 16px; text-decoration: none; color: grey;');
 | 
						|
          a.appendChild(span);
 | 
						|
          var link = document.createTextNode('🔗');
 | 
						|
          span.appendChild(link);
 | 
						|
          element.insertBefore(a, element.firstChild);
 | 
						|
 | 
						|
          // remove the link icon when the mouse is moved away,
 | 
						|
          // but keep it shown if the mouse is over the element, the link or the icon
 | 
						|
          hide = function(evt) {
 | 
						|
            if (document.elementFromPoint(evt.clientX, evt.clientY) != element
 | 
						|
                && document.elementFromPoint(evt.clientX, evt.clientY) != a
 | 
						|
                && document.elementFromPoint(evt.clientX, evt.clientY) != span
 | 
						|
                && document.elementFromPoint(evt.clientX, evt.clientY) != link
 | 
						|
                && element.contains(a)) {
 | 
						|
              element.removeChild(a);
 | 
						|
            }
 | 
						|
          }
 | 
						|
          element.onmouseout = hide;
 | 
						|
          a.onmouseout = hide;
 | 
						|
          span.onmouseout = hide;
 | 
						|
          link.onmouseout = hide;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    function getExcludedIds() {
 | 
						|
      var excluded = {};
 | 
						|
      excluded['header'] = true;
 | 
						|
      excluded['toc'] = true;
 | 
						|
      excluded['toctitle'] = true;
 | 
						|
      excluded['content'] = true;
 | 
						|
      excluded['preamble'] = true;
 | 
						|
      excluded['footer'] = true;
 | 
						|
      excluded['footer-text'] = true;
 | 
						|
      return excluded;
 | 
						|
    }
 | 
						|
</script>
 | 
						|
 | 
						|
++++
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
opts = OptionParser()
 | 
						|
opts.add_option('-o', '--out', help='output file')
 | 
						|
opts.add_option('-s', '--src', help='source file')
 | 
						|
opts.add_option('-x', '--suffix', help='suffix for included filenames')
 | 
						|
opts.add_option('-b', '--searchbox', action="store_true", default=True,
 | 
						|
                help="generate the search boxes")
 | 
						|
opts.add_option('--no-searchbox', action="store_false", dest='searchbox',
 | 
						|
                help="don't generate the search boxes")
 | 
						|
options, _ = opts.parse_args()
 | 
						|
 | 
						|
try:
 | 
						|
  try:
 | 
						|
    out_file = open(options.out, 'w', errors='ignore')
 | 
						|
    src_file = open(options.src, 'r', errors='ignore')
 | 
						|
  except TypeError:
 | 
						|
    out_file = open(options.out, 'w')
 | 
						|
    src_file = open(options.src, 'r')
 | 
						|
  last_line = ''
 | 
						|
  ignore_next_line = False
 | 
						|
  last_title = ''
 | 
						|
  for line in src_file:
 | 
						|
    if PAT_GERRIT.match(last_line):
 | 
						|
      # Case of "GERRIT\n------" at the footer
 | 
						|
      out_file.write(GERRIT_UPLINK)
 | 
						|
      last_line = ''
 | 
						|
    elif PAT_SEARCHBOX.match(last_line):
 | 
						|
      # Case of 'SEARCHBOX\n---------'
 | 
						|
      if options.searchbox:
 | 
						|
        out_file.write(SEARCH_BOX)
 | 
						|
      last_line = ''
 | 
						|
    elif PAT_INCLUDE.match(line):
 | 
						|
      # Case of 'include::<filename>'
 | 
						|
      match = PAT_INCLUDE.match(line)
 | 
						|
      out_file.write(last_line)
 | 
						|
      last_line = match.group(1) + options.suffix + match.group(2) + '\n'
 | 
						|
    elif PAT_STARS.match(line):
 | 
						|
      if PAT_TITLE.match(last_line):
 | 
						|
        # Case of the title in '.<title>\n****\nget::<url>\n****'
 | 
						|
        match = PAT_TITLE.match(last_line)
 | 
						|
        last_title = GET_TITLE % match.group(1)
 | 
						|
      else:
 | 
						|
        out_file.write(last_line)
 | 
						|
        last_title = ''
 | 
						|
    elif PAT_GET.match(line):
 | 
						|
      # Case of '****\nget::<url>\n****' in rest api
 | 
						|
      url = PAT_GET.match(line).group(1)
 | 
						|
      out_file.write(GET_MACRO.format(url) % last_title)
 | 
						|
      ignore_next_line = True
 | 
						|
    elif ignore_next_line:
 | 
						|
      # Handle the trailing '****' of the 'get::' case
 | 
						|
      last_line = ''
 | 
						|
      ignore_next_line = False
 | 
						|
    else:
 | 
						|
      out_file.write(last_line)
 | 
						|
      last_line = line
 | 
						|
  out_file.write(last_line)
 | 
						|
  out_file.write(LINK_SCRIPT)
 | 
						|
  out_file.close()
 | 
						|
except IOError as err:
 | 
						|
  sys.stderr.write(
 | 
						|
      "error while expanding %s to %s: %s" % (options.src, options.out, err))
 | 
						|
  exit(1)
 |