From 3beb6a408671c5030c5cfd805d0318981c666434 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 27 Apr 2023 09:29:42 -0600 Subject: Cleanup the SCons build a bit * Removes the old bootstrap.py file. The still-used manifest parser is moved inline into doc/SConscript * Some reformatting and license text updates, as well as fstringinfying. * Help text is modified, adding the recent zipapp option, and indent changes let more stuff fit on the screen. * Aliases added for most of the build targets listed in the help msg. The ones now listed all work when given as cli targets, except the the two full source balls: scons: *** Do not know how to make File target `src-tar-gz' (/home/mats/github/scons/src-tar-gz). Stop. scons: *** Do not know how to make File target `src-zip' (/home/mats/github/scons/src-zip). Stop. Should these two be removed from the listing? I think we get the former directly from github, and the latter hasn't been around for a while. Fixes #4341 Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 + RELEASE.txt | 5 +- SCons/Tool/docbook/docbook.xml | 4 +- SCons/Tool/docbook/docs/SConstruct | 14 +- SCons/Tool/docbook/docs/html.xsl | 33 +--- SCons/Tool/docbook/docs/manual.xml | 47 ++--- SCons/Tool/docbook/docs/pdf.xsl | 34 +--- SCons/Tool/docbook/utils/xmldepend.xsl | 11 +- SConstruct | 121 ++++++------ bootstrap.py | 233 ---------------------- doc/SConscript | 345 ++++++++++++++++++++------------- site_scons/BuildCommandLine.py | 153 ++++++++++----- site_scons/SConsRevision.py | 8 +- site_scons/Utilities.py | 18 +- site_scons/epydoc.py | 32 +-- site_scons/scons_local_package.py | 65 +++---- site_scons/site_init.py | 4 + site_scons/soe_utils.py | 10 +- site_scons/update_build_info.py | 18 +- site_scons/zip_utils.py | 25 +-- 20 files changed, 500 insertions(+), 682 deletions(-) delete mode 100755 bootstrap.py diff --git a/CHANGES.txt b/CHANGES.txt index 3725fa4..c1f3a2a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -124,6 +124,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Cleaned up dblite module (checker warnings, etc.). - Some cleanup in the FortranCommon tool. - Changed the message about scons -H to clarify it shows built-in options. + - Release-building setup tweaked. (most of) the targets listed in + SCons' own "scons --help" now work again. From Jonathon Reinhart: - Fix another instance of `int main()` in CheckLib() causing failures diff --git a/RELEASE.txt b/RELEASE.txt index bffe740..9e22180 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -96,7 +96,10 @@ IMPROVEMENTS PACKAGING --------- -- List changes in the way SCons is packaged and/or released +- The build of scons now matches the help text displayed - the targets + listed there can all now be given as a target to build (except for + the two full source balls; the source tar.gz is currently generated directly + by the GitHub release process). DOCUMENTATION ------------- diff --git a/SCons/Tool/docbook/docbook.xml b/SCons/Tool/docbook/docbook.xml index b0de859..c367520 100644 --- a/SCons/Tool/docbook/docbook.xml +++ b/SCons/Tool/docbook/docbook.xml @@ -1,6 +1,8 @@ + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:fo="http://www.w3.org/1999/XSL/Format" + version="1.0"> - + @@ -51,5 +32,5 @@ reference toc,title set toc,title - + diff --git a/SCons/Tool/docbook/docs/manual.xml b/SCons/Tool/docbook/docs/manual.xml index 64ee925..642b8bd 100644 --- a/SCons/Tool/docbook/docs/manual.xml +++ b/SCons/Tool/docbook/docs/manual.xml @@ -1,27 +1,8 @@ @@ -49,7 +30,7 @@ stylesheet utils/xmldepend.xsl by Paul DuBois is used for thi Note, that there is no support for XML catalog resolving offered! This tool calls the XSLT processors and PDF renderers with the stylesheets you specified, that's it. The rest lies in your hands and you still have to know what you're doing when -resolving names via a catalog. +resolving names via a catalog.
Install Installing it, requires you to copy (or, even better: checkout) the contents of the @@ -62,7 +43,7 @@ package's docbook folder to -For more infos about this, please refer to +For more infos about this, please refer to the SCons User's Guide, chap. 19.7 "Where to put your custom Builders and Tools" and @@ -131,7 +112,7 @@ Tool uses the given names as file stems, and adds the suffixes for target and so accordingly. -The rules given above are valid for the Builders DocbookHtml, DocbookPdf, +The rules given above are valid for the Builders DocbookHtml, DocbookPdf, DocbookSlidePdf and DocbookXInclude. For the DocbookMan transformation you can specify a target name, but the actual output names are automatically set from the refname entries in your XML source. @@ -149,13 +130,13 @@ Builder call as follows: e.g. html.xsl for HTML and pdf.xsl for PDF output, a set of variables for setting the default XSL name is provided. These are: -DOCBOOK_DEFAULT_XSL_HTML -DOCBOOK_DEFAULT_XSL_HTMLCHUNKED -DOCBOOK_DEFAULT_XSL_HTMLHELP -DOCBOOK_DEFAULT_XSL_PDF -DOCBOOK_DEFAULT_XSL_MAN -DOCBOOK_DEFAULT_XSL_SLIDESPDF -DOCBOOK_DEFAULT_XSL_SLIDESHTML +DOCBOOK_DEFAULT_XSL_HTML +DOCBOOK_DEFAULT_XSL_HTMLCHUNKED +DOCBOOK_DEFAULT_XSL_HTMLHELP +DOCBOOK_DEFAULT_XSL_PDF +DOCBOOK_DEFAULT_XSL_MAN +DOCBOOK_DEFAULT_XSL_SLIDESPDF +DOCBOOK_DEFAULT_XSL_SLIDESHTML and you can set them when constructing your environment: @@ -178,7 +159,7 @@ XSL transformation is not picked up by the stylesheets. -As a result, there is simply no use in specifying a target HTML name. +As a result, there is simply no use in specifying a target HTML name. So the basic syntax for these builders is: env = Environment(tools=['docbook']) @@ -199,7 +180,7 @@ call, and the given prefix gets prepended to all the created filenames: env.DocbookHtmlhelp('manual', xsl='htmlhelp.xsl', base_dir='output/') Make sure that you don't forget the trailing slash for the base folder, else -your files get renamed only! +your files get renamed only!
All builders diff --git a/SCons/Tool/docbook/docs/pdf.xsl b/SCons/Tool/docbook/docs/pdf.xsl index 8703ced..63008d5 100644 --- a/SCons/Tool/docbook/docs/pdf.xsl +++ b/SCons/Tool/docbook/docs/pdf.xsl @@ -1,35 +1,15 @@ - + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:fo="http://www.w3.org/1999/XSL/Format" + version="1.0"> - + @@ -58,5 +38,5 @@ set toc,title - + diff --git a/SCons/Tool/docbook/utils/xmldepend.xsl b/SCons/Tool/docbook/utils/xmldepend.xsl index e70e1b5..b2f4ee2 100644 --- a/SCons/Tool/docbook/utils/xmldepend.xsl +++ b/SCons/Tool/docbook/utils/xmldepend.xsl @@ -1,7 +1,10 @@ - @@ -153,4 +156,4 @@ TODO: - \ No newline at end of file + diff --git a/SConstruct b/SConstruct index d4659be..b414532 100644 --- a/SConstruct +++ b/SConstruct @@ -1,17 +1,3 @@ -# -# SConstruct file to build scons packages during development. -# -# See the README.rst file for an overview of how SCons is built and tested. -import os.path -import sys -import textwrap -from time import strftime - -copyright_years = strftime('2001 - %Y') - -# This gets inserted into the man pages to reflect the month of release. -month_year = strftime('%B %Y') -# # MIT License # # Copyright The SCons Foundation @@ -34,30 +20,42 @@ month_year = strftime('%B %Y') # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# +"""SConstruct file to build scons packages during development/release.""" + +# See the README.rst file for an overview of how SCons is built and tested. + +import os.path +import sys +import textwrap +from time import strftime + +copyright_years = strftime('2001 - %Y') + +# This gets inserted into the man pages to reflect the month of release. +month_year = strftime('%B %Y') project = 'scons' default_version = '4.5.3' -copyright = "Copyright (c) %s The SCons Foundation" % copyright_years +copyright = f"Copyright (c) {copyright_years} The SCons Foundation" -# # We let the presence or absence of various utilities determine whether # or not we bother to build certain pieces of things. This should allow # people to still do SCons packaging work even if they don't have all # of the utilities installed -# -print("git :%s" % git) -print("gzip :%s" % gzip) -print("unzip :%s" % unzip) -print("zip :%s" % zip_path) +print(f"git :{git}") +print(f"gzip :{gzip}") +print(f"unzip :{unzip}") +print(f"zip :{zip_path}") # # Adding some paths to sys.path, this is mainly needed # for the doc toolchain. # -addpaths = [os.path.abspath(os.path.join(os.getcwd(), 'bin')), - os.path.abspath(os.path.join(os.getcwd(), 'testing/framework'))] +addpaths = [ + os.path.abspath(os.path.join(os.getcwd(), 'bin')), + os.path.abspath(os.path.join(os.getcwd(), 'testing/framework')), +] for a in addpaths: if a not in sys.path: sys.path.append(a) @@ -71,16 +69,37 @@ Default('.', command_line.build_dir) SetOption('duplicate', 'copy') packaging_flavors = [ - ('tar-gz', "The normal .tar.gz file for end-user installation."), - ('local-tar-gz', "A .tar.gz file for dropping into other software " + - "for local use."), - ('zip', "The normal .zip file for end-user installation."), - ('local-zip', "A .zip file for dropping into other software " + - "for local use."), - ('src-tar-gz', "A .tar.gz file containing all the source " + - "(including tests and documentation)."), - ('src-zip', "A .zip file containing all the source " + - "(including tests and documentation)."), + ( + 'wheel', + "The Python wheel file for pip installations." + ), + ( + 'tar-gz', + "The normal .tar.gz file for end-user installation." + ), + ( + 'local-tar-gz', + "A .tar.gz file for dropping into other software for local use.", + ), + ( + 'zip', + "The normal .zip file for end-user installation."), + ( + 'local-zip', + "A .zip file for dropping into other software for local use." + ), + ( + 'src-tar-gz', + "A .tar.gz file containing all the source (including tests and documentation).", + ), + ( + 'src-zip', + "A .zip file containing all the source (including tests and documentation).", + ), + ( + 'local-zipapp', + "A .pyz local zipapp. Like local-zip without need to unpack.", + ) ] test_tar_gz_dir = os.path.join(command_line.build_dir, "test-tar-gz") @@ -102,7 +121,7 @@ else: # python_project_subinst_dir = os.path.join("lib", project) # project_script_subinst_dir = 'bin' -indent_fmt = ' %-26s ' +indent_fmt = ' %-14s ' Help("""\ The following aliases build packages of various types, and unpack the @@ -135,12 +154,10 @@ for variable, help_text in command_line.command_line_variables: Help(tw.fill(help_text) + '\n') revaction = SCons_revision -revbuilder = Builder(action=Action(SCons_revision, - varlist=['COPYRIGHT', 'VERSION'])) +revbuilder = Builder(action=Action(SCons_revision, varlist=['COPYRIGHT', 'VERSION'])) env = Environment( ENV=command_line.ENV, - BUILD=command_line.build_id, BUILDDIR=command_line.build_dir, BUILDSYS=command_line.build_system, @@ -148,54 +165,37 @@ env = Environment( COPYRIGHT=copyright, DATE=command_line.date, DEB_DATE=deb_date, - DEVELOPER=command_line.developer, DISTDIR=os.path.join(command_line.build_dir, 'dist'), MONTH_YEAR=month_year, REVISION=command_line.revision, VERSION=command_line.version, - TAR_HFLAG=tar_hflag, - ZIP=zip_path, ZIPFLAGS='-r', UNZIP=unzip, UNZIPFLAGS='-o -d $UNPACK_ZIP_DIR', - # ZCAT=zcat, # ZIPID=zipit, - TEST_SRC_TAR_GZ_DIR=test_src_tar_gz_dir, TEST_SRC_ZIP_DIR=test_src_zip_dir, TEST_TAR_GZ_DIR=test_tar_gz_dir, TEST_ZIP_DIR=test_zip_dir, - UNPACK_TAR_GZ_DIR=unpack_tar_gz_dir, UNPACK_ZIP_DIR=unpack_zip_dir, - - BUILDERS={'SCons_revision': revbuilder, - 'SOElim': soelimbuilder}, - - PYTHON='"%s"' % sys.executable, + BUILDERS={'SCons_revision': revbuilder, 'SOElim': soelimbuilder}, + PYTHON=f'"{sys.executable}"', PYTHONFLAGS='-tt', ) Version_values = [Value(command_line.version), Value(command_line.build_id)] - installed_local_files = create_local_packages(env) - update_init_file(env) -# -# -# -# -# Documentation. -# -Export('command_line', 'env', 'whereis', 'revaction') +# Documentation +Export('command_line', 'env', 'whereis', 'revaction') SConscript('doc/SConscript') - # Copy manpages into base dir for inclusion in pypi packages man_pages = env.Install("#/", Glob("#/build/doc/man/*.1")) @@ -208,6 +208,8 @@ wheel = env.Command( source=['setup.cfg', 'setup.py', 'SCons/__init__.py'] + man_pages, action='$PYTHON -m build --outdir $DISTDIR', ) +env.Alias("wheel", wheel[0]) +env.Alias("tar-gz", wheel[1]) # TODO: this is built the old way, because "build" doesn't make zipfiles, # and it deletes its isolated env so we can't just zip that one up. @@ -216,6 +218,7 @@ zip_file = env.Command( source=['setup.cfg', 'setup.py', 'SCons/__init__.py'] + man_pages, action='$PYTHON setup.py sdist --format=zip', ) +env.Alias("zip", zip_file) # Now set depends so the above run in a particular order # NOTE: 'build' with default options builds sdist, then whl from sdist, diff --git a/bootstrap.py b/bootstrap.py deleted file mode 100755 index 3bf53ea..0000000 --- a/bootstrap.py +++ /dev/null @@ -1,233 +0,0 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -"""bootstrap.py - -Execute SCons from this source tree. It copies Python scripts and modules -from src/ subdirectory into a subdirectory named "bootstrap/" (by default), -and executes SCons from there with the supplied command-line arguments. - -This is a minimal build of SCons to bootstrap the full build of all the -packages, as specified in our local SConstruct file. - -Some options are specific to this bootstrap.py script and are *not* passed -on to the SCons script. All of these begin with the string "bootstrap_": - - --bootstrap_dir=DIR - - Sets the name of the directory into which the SCons files will - be copied. The default is "bootstrap" in the local subdirectory. - - --bootstrap_force - - Forces a copy of all necessary files. By default, the - bootstrap.py script only updates the bootstrap copy if the - content of the source copy is different. - - --bootstrap_src=DIR - - Searches for the SCons files relative to the specified DIR, - then relative to the directory in which this bootstrap.py - script is found. - - --bootstrap_update - - Only updates the bootstrap subdirectory, and then exits. - -In addition to the above, the bootstrap.py script understands -the following SCons options: - - -C, --directory - - Changes to the specified directory before invoking SCons. - Because we change directory right away to the specified directory, - the SCons script itself doesn't need to, so this option gets - "eaten" by the bootstrap.py script. -""" - -import os -import os.path -import sys -import glob -import subprocess -import filecmp -import shutil - -def parseManifestLines(basedir, manifest): - """ - Scans a MANIFEST file, and returns the list of source files. - - Has basic support for recursive globs '**', - filename wildcards of the form '*.xml' and - comment lines, starting with a '#'. - - :param basedir: base path to find files in. Note this does not - run in an SCons context so path must not need - further processing (e.g. no '#' signs) - :param manifest: path to manifest file - :returns: list of files - """ - sources = [] - basewd = os.path.abspath(basedir) - with open(manifest) as m: - lines = m.readlines() - for l in lines: - if l.startswith('#'): - # Skip comments - continue - l = l.rstrip('\n') - if l.endswith('**'): - # Glob all files recursively - globwd = os.path.dirname(os.path.join(basewd, l)) - for path, dirs, files in os.walk(globwd): - for f in files: - fpath = os.path.join(globwd, path, f) - sources.append(os.path.relpath(fpath, basewd)) - elif '*' in l: - # Glob file pattern - files = glob.glob(os.path.join(basewd, l)) - for f in files: - sources.append(os.path.relpath(f, basewd)) - else: - sources.append(l) - - return sources - -def main(): - script_dir = os.path.abspath(os.path.dirname(__file__)) - - bootstrap_dir = os.path.join(script_dir, 'bootstrap') - - pass_through_args = [] - update_only = None - - requires_an_argument = 'bootstrap.py: %s requires an argument\n' - - search = [script_dir] - - def find(filename, search=search): - for dir_name in search: - filepath = os.path.join(dir_name, filename) - if os.path.exists(filepath): - return os.path.normpath(filepath) - sys.stderr.write("could not find `%s' in search path:\n" % filename) - sys.stderr.write("\t" + "\n\t".join(search) + "\n") - sys.exit(2) - - def must_copy(dst, src): - if not os.path.exists(dst): - return 1 - return not filecmp.cmp(dst,src) - - # Note: We don't use the getopt module to process the command-line - # arguments because we'd have to teach it about all of the SCons options. - - command_line_args = sys.argv[1:] - - while command_line_args: - arg = command_line_args.pop(0) - - if arg == '--bootstrap_dir': - try: - bootstrap_dir = command_line_args.pop(0) - except IndexError: - sys.stderr.write(requires_an_argument % arg) - sys.exit(1) - elif arg[:16] == '--bootstrap_dir=': - bootstrap_dir = arg[16:] - elif arg == '--bootstrap_force': - def must_copy(dst, src): - return 1 - elif arg == '--bootstrap_src': - try: - search.insert(0, command_line_args.pop(0)) - except IndexError: - sys.stderr.write(requires_an_argument % arg) - sys.exit(1) - elif arg[:16] == '--bootstrap_src=': - search.insert(0, arg[16:]) - elif arg == '--bootstrap_update': - update_only = 1 - elif arg in ('-C', '--directory'): - try: - dir = command_line_args.pop(0) - except IndexError: - sys.stderr.write(requires_an_argument % arg) - sys.exit(1) - else: - os.chdir(dir) - elif arg[:2] == '-C': - os.chdir(arg[2:]) - elif arg[:12] == '--directory=': - os.chdir(arg[12:]) - else: - pass_through_args.append(arg) - - scons_py = os.path.join('scripts', 'scons.py') - src_engine = os.path.join('src', 'engine') - MANIFEST_in = find(os.path.join(src_engine, 'MANIFEST.in')) - manifest_files = [os.path.join(src_engine, x) - for x in parseManifestLines(os.path.join(script_dir, src_engine), - MANIFEST_in)] - - files = [scons_py] + manifest_files - - for filename in files: - src = find(filename) - dst = os.path.join(bootstrap_dir, filename) - if must_copy(dst, src): - dir = os.path.split(dst)[0] - if not os.path.isdir(dir): - os.makedirs(dir) - try: - os.unlink(dst) - except Exception as e: - pass - - shutil.copyfile(src, dst) - - if update_only: - sys.exit(0) - - args = [sys.executable, os.path.join(bootstrap_dir, scons_py)] + pass_through_args - - sys.stdout.write(" ".join(args) + '\n') - sys.stdout.flush() - - os.environ['SCONS_LIB_DIR'] = os.path.join(bootstrap_dir, src_engine) - - sys.exit(subprocess.Popen(args, env=os.environ).wait()) - - -if __name__ == "__main__": - print("Please use") - print("python scripts/scons.py") - print("Instead of python bootstrap.py. Bootstrap.py is obsolete") - sys.exit(-1) - main() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/doc/SConscript b/doc/SConscript index 82c696e..cb92b41 100644 --- a/doc/SConscript +++ b/doc/SConscript @@ -1,7 +1,3 @@ -# -# SConscript file for building SCons documentation. -# - # MIT License # # Copyright The SCons Foundation @@ -25,12 +21,17 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +"""Build SCons documentation.""" + +import glob import os.path import re +import shutil import sys -import glob +import time -import bootstrap +import SCons.Builder +import SCons.Util Import('command_line', 'env', 'whereis', 'revaction') @@ -44,7 +45,7 @@ try: import SConsExamples except ImportError as exc: print("doc: SConsDoc failed to import, the error was:") - print(" ImportError: %s" % exc) + print(f" ImportError: {exc}") print(" Please make sure that python-lxml is installed.") skip_doc = True @@ -77,30 +78,27 @@ tar_list = [] orig_env = env env = orig_env.Clone(SCONS_PY=File('#/scripts/scons.py').rfile()) - # # --- Helpers --- # def writeVersionXml(verfile, date, ver, rev, copyright_years): - """ Helper function: Write a version.xml file. """ + """Helper function: Write a version.xml file.""" try: os.unlink(verfile) except OSError: pass # okay if the file didn't exist dir, f = os.path.split(verfile) - try: - os.makedirs(dir) - except OSError: - pass # okay if the directory already exists + os.makedirs(dir, exist_ok=True) with open(verfile, "w") as vf: - vf.write(""" - - - - -""" % (date, ver, rev, copyright_years)) + + + + +""") # The names of the target files for the MAN pages @@ -117,13 +115,12 @@ man_replace_tpl = r""".TH "%(uctitle)s" "1" "%(today)s" "SCons %(version)s" "SCo .SH "NOTE" %(title)s \- This is a replacement file, stemming from an incomplete packaging process without the required doc modules installed. Please -update the system and try running bootstrap.py again. +update the system and try running the build again. """ # # --- Processing --- # - if skip_doc: print("doc: ...skipping building User Guide.") print(" ...creating fake MAN pages.") @@ -135,42 +132,43 @@ if skip_doc: if not os.path.isdir(scdir): os.makedirs(scdir) - import time - - today = time.strftime("%Y-%m-%d", - time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))) + today = time.strftime( + "%Y-%m-%d", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))) + ) version = env.subst('$VERSION') for m in man_page_list: man, _ = os.path.splitext(m) - fman = open(os.path.join(scdir, m), "w") - fman.write(man_replace_tpl % {'uctitle': man.upper().replace("-", "\\-"), - 'today': today, - 'title': man, - 'version': version}) - fman.close() + with open(os.path.join(scdir, m), "w") as fman: + fman.write( + man_replace_tpl + % { + 'uctitle': man.upper().replace("-", "\\-"), + 'today': today, + 'title': man, + 'version': version, + } + ) else: if not lynx: - print("doc: Warning, lynx is not installed...created release packages won't be complete!") + print( + "doc: Warning, lynx is not installed. " + "Created release packages will not be complete!" + ) - # # Always create a version.xml file containing the version information # for this run. Ignore it for dependency purposes so we don't # rebuild all the docs every time just because the date changes. - # - date, ver, rev, copyright_years = env.Dictionary('DATE', 'VERSION', 'REVISION', 'COPYRIGHT_YEARS') + date, ver, rev, copyright_years = env.Dictionary( + 'DATE', 'VERSION', 'REVISION', 'COPYRIGHT_YEARS' + ) version_xml = File(os.path.join(build, "version.xml")) writeVersionXml(str(version_xml), date, ver, rev, copyright_years) - import shutil - import SCons.Builder - import SCons.Util - - - # - # Builder for copying files to an Install dir, based - # on their extension (better: glob matching pattern)... - # def _glob_install_action(target, source, env): + """Builder for copying files to an Install dir. + + Selection is based on globbing for filename extension. + """ if not SCons.Util.is_List(target): target = [target] if not SCons.Util.is_List(source): @@ -178,8 +176,8 @@ else: for t, s in zip(target, source): shutil.copy(str(s), str(t)) - def _glob_install_emitter(target, source, env): + """Emitter for GlobInstall Builder.""" if not SCons.Util.is_List(target): target = [target] if not SCons.Util.is_List(source): @@ -194,16 +192,13 @@ else: res_src.append(g) return res, res_src - - _glob_install_builder = SCons.Builder.Builder(action=_glob_install_action, - emitter=_glob_install_emitter) + _glob_install_builder = SCons.Builder.Builder( + action=_glob_install_action, emitter=_glob_install_emitter + ) env['BUILDERS']['GlobInstall'] = _glob_install_builder - - # - # Builder for copying ChunkedHTML files to an Install dir... - # def _chunked_install_action(target, source, env): + """Builder for copying ChunkedHTML files to an Install dir.""" if not SCons.Util.is_List(target): target = [target] if not SCons.Util.is_List(source): @@ -213,8 +208,8 @@ else: for g in glob.glob(spattern): shutil.copy(g, tdir) - def _chunked_install_emitter(target, source, env): + """Emitter for ChunkedInstall Builder.""" if not SCons.Util.is_List(target): target = [target] if not SCons.Util.is_List(source): @@ -224,9 +219,9 @@ else: head, tail = os.path.split(str(source[0])) return os.path.join(str(tdir), tail), source - - _chunked_install_builder = SCons.Builder.Builder(action=_chunked_install_action, - emitter=_chunked_install_emitter) + _chunked_install_builder = SCons.Builder.Builder( + action=_chunked_install_action, emitter=_chunked_install_emitter + ) env['BUILDERS']['ChunkedInstall'] = _chunked_install_builder if not env.GetOption('clean'): @@ -246,16 +241,17 @@ else: print("OK") else: print( - "Not all example names and suffixes are unique! Please correct the errors listed above and try again.") + "Not all example names and suffixes are unique! " + "Please correct the errors listed above and try again." + ) sys.exit(1) # List of prerequisite files in the build/doc folder buildsuite = [] - def copy_dbfiles(env, toolpath, paths, fpattern, use_builddir=True): - """ Helper function, copies a bunch of files matching - the given fpattern to a target directory. + """Helper function, copies a bunch of files matching + the given fpattern to a target directory. """ global buildsuite if not SCons.Util.is_List(toolpath): @@ -266,14 +262,19 @@ else: fpattern = [fpattern] if use_builddir: - target_dir = env.Dir(os.path.join(command_line.build_dir, *(toolpath + paths))) - buildsuite.extend(env.GlobInstall(target_dir, - os.path.join('..', *(toolpath + paths + fpattern)))) + target_dir = env.Dir( + os.path.join(command_line.build_dir, *(toolpath + paths)) + ) + buildsuite.extend( + env.GlobInstall( + target_dir, os.path.join('..', *(toolpath + paths + fpattern)) + ) + ) else: target_dir = env.Dir(os.path.join(*(toolpath + paths))) - buildsuite.extend(env.GlobInstall(target_dir, - os.path.join(*(paths + fpattern)))) - + buildsuite.extend( + env.GlobInstall(target_dir, os.path.join(*(paths + fpattern))) + ) # # Copy generated files (.gen/.mod/.xml) to the build folder @@ -310,13 +311,13 @@ else: copy_dbfiles(env, toolpath, [], 'gs.py') copy_dbfiles(env, toolpath, [], 'zip.py') - # # Each document will live in its own subdirectory "build/doc/xxx". - # List them here by their subfolder names. Note, how the specifiers + # List them here by their subfolder names. Note how the specifiers # for each subdir (=DOCTARGETS) have nothing to do with which - # formats get created...but which of the outputs get installed + # formats get created, but which of the outputs get installed # to the build folder and added to the different source and binary # packages in the end. + # # In addition to the list of target formats (DOCTARGETS), we also # store some dependency information in this dict. The DOCDEPENDS # list contains all files from each local "MANIFEST", after @@ -325,51 +326,91 @@ else: # such that a simple 'python bootstrap.py' rebuilds the # documentation when a file, like 'doc/user/depends.xml' # for example, changes. + # # Finally, in DOCNODES we store the created PDF and HTML files, # such that we can then install them in the proper places for # getting picked up by the archiving/packaging stages. DOCTARGETS = 0 DOCDEPENDS = 1 DOCNODES = 2 - docs = {'design': (['chunked', 'pdf'], [], []), - # 'python10' : (['chunked','html','pdf'], [], []), - 'reference': (['chunked', 'html', 'pdf'], [], []), - # 'developer' : (['chunked','html','pdf'], [], []), - 'user': (['chunked', 'html', 'pdf', 'epub', 'text'], [], []), - 'man': (['man', 'epub', 'text'], [], []) - } + docs = { + # 'design': (['chunked', 'pdf'], [], []), + # 'python10' : (['chunked','html','pdf'], [], []), + # 'reference': (['chunked', 'html', 'pdf'], [], []), + # 'developer' : (['chunked','html','pdf'], [], []), + 'user': (['chunked', 'html', 'pdf', 'epub', 'text'], [], []), + 'man': (['man', 'epub', 'text'], [], []), + } # # We have to tell SCons to scan the top-level XML files which # get included by the document XML files in the subdirectories. # + + def _parse_manifest_lines(basedir, manifest) -> list: + """ + Scans a MANIFEST file, and returns the list of source files. + + Has basic support for recursive globs '**', + filename wildcards of the form '*.xml' and + comment lines, starting with a '#'. + + Args: + basedir: base path to find files in. Note this does not + run in an SCons context so path must not need + further processing (e.g. no '#' signs) + manifest: path to manifest file + """ + sources = [] + basewd = os.path.abspath(basedir) + with open(manifest) as m: + lines = m.readlines() + for l in lines: + if l.startswith('#'): + # Skip comments + continue + l = l.rstrip('\n') + if l.endswith('**'): + # Glob all files recursively + globwd = os.path.dirname(os.path.join(basewd, l)) + for path, dirs, files in os.walk(globwd): + for f in files: + fpath = os.path.join(globwd, path, f) + sources.append(os.path.relpath(fpath, basewd)) + elif '*' in l: + # Glob file pattern + files = glob.glob(os.path.join(basewd, l)) + for f in files: + sources.append(os.path.relpath(f, basewd)) + else: + sources.append(l) + + return sources + + manifest = File('MANIFEST').rstr() - src_files = bootstrap.parseManifestLines('.', manifest) + src_files = _parse_manifest_lines('.', manifest) for s in src_files: if not s: continue base, ext = os.path.splitext(s) if ext in ['.fig', '.jpg']: - buildsuite.extend(env.Command(os.path.join(build, s), - s, - Copy("$TARGET", "$SOURCE"))) + buildsuite.extend( + env.Command(os.path.join(build, s), s, Copy("$TARGET", "$SOURCE")) + ) else: - revaction([env.File(os.path.join(build, s))], - [env.File(s)], env) + revaction([env.File(os.path.join(build, s))], [env.File(s)], env) for doc in docs: - - # - # Read MANIFEST file and copy the listed files to the - # build directory, while branding them with the - # SCons copyright and the current revision number... - # + # Read MANIFEST file and copy the listed files to the build directory, + # while branding them with the SCons copyright and the current + # revision number... if not os.path.exists(os.path.join(build, doc)): env.Execute(Mkdir(os.path.join(build, doc))) if not os.path.exists(os.path.join(build, doc, 'titlepage')): env.Execute(Mkdir(os.path.join(build, doc, 'titlepage'))) manifest = File(os.path.join(doc, 'MANIFEST')).rstr() - src_files = bootstrap.parseManifestLines(doc, manifest) + src_files = _parse_manifest_lines(doc, manifest) for s in src_files: if not s: continue @@ -382,8 +423,9 @@ else: else: target_dir = os.path.join(build, doc) if ext in ['.fig', '.jpg', '.svg']: - docs[doc][DOCDEPENDS].extend(env.Command(build_s, doc_s, - Copy("$TARGET", "$SOURCE"))) + docs[doc][DOCDEPENDS].extend( + env.Command(build_s, doc_s, Copy("$TARGET", "$SOURCE")) + ) else: btarget = env.File(build_s) docs[doc][DOCDEPENDS].append(btarget) @@ -406,38 +448,48 @@ else: if 'html' in docs[doc][DOCTARGETS]: sctargets.append(env.File(os.path.join(scdir, 'index.html'))) if 'chunked' in docs[doc][DOCTARGETS]: - sctargets.append(env.File(os.path.join(scdir, 'scons-%s' % doc, 'index.html'))) + sctargets.append( + env.File(os.path.join(scdir, f'scons-{doc}', 'index.html')) + ) if 'pdf' in docs[doc][DOCTARGETS]: - sctargets.append(env.File(os.path.join(scdir, 'scons-%s.pdf' % doc))) + sctargets.append(env.File(os.path.join(scdir, f'scons-{doc}.pdf'))) if 'epub' in docs[doc][DOCTARGETS]: - sctargets.append(env.File(os.path.join(scdir, 'scons-%s.epub' % doc))) + sctargets.append(env.File(os.path.join(scdir, f'scons-{doc}.epub'))) if 'man' in docs[doc][DOCTARGETS]: for m in man_page_list: sctargets.append(os.path.join(scdir, m)) man, _1 = os.path.splitext(m) - sctargets.append(os.path.join(scdir, 'scons-%s.pdf' % man)) - sctargets.append(os.path.join(scdir, 'scons-%s.html' % man)) + sctargets.append(os.path.join(scdir, f'scons-{man}.pdf')) + sctargets.append(os.path.join(scdir, f'scons-{man}.html')) - docs[doc][DOCNODES].extend(env.Command(sctargets, buildsuite + docs[doc][DOCDEPENDS], - "cd %s && $PYTHON ${SCONS_PY.abspath}%s" % (scdir, cleanopt))) + docs[doc][DOCNODES].extend( + env.Command( + target=sctargets, + source=buildsuite + docs[doc][DOCDEPENDS], + action="cd %s && $PYTHON ${SCONS_PY.abspath}%s" % (scdir, cleanopt), + ) + ) install_css = False for doc in docs: # Collect the output files for this subfolder - htmldir = os.path.join(build, 'HTML', 'scons-%s' % doc) + htmldir = os.path.join(build, 'HTML', f'scons-{doc}') htmlindex = os.path.join(htmldir, 'index.html') - html = os.path.join(build, 'HTML', 'scons-%s.html' % doc) - pdf = os.path.join(build, 'PDF', 'scons-%s.pdf' % doc) - epub = os.path.join(build, 'EPUB', 'scons-%s.epub' % doc) - text = os.path.join(build, 'TEXT', 'scons-%s.txt' % doc) + html = os.path.join(build, 'HTML', f'scons-{doc}.html') + pdf = os.path.join(build, 'PDF', f'scons-{doc}.pdf') + epub = os.path.join(build, 'EPUB', f'scons-{doc}.epub') + text = os.path.join(build, 'TEXT', f'scons-{doc}.txt') if 'chunked' in docs[doc][DOCTARGETS]: - installed_chtml = env.ChunkedInstall(env.Dir(htmldir), - os.path.join(build, doc, 'scons-%s' % doc, 'index.html')) - installed_chtml_css = env.Install(env.Dir(htmldir), - os.path.join(build, doc, 'scons.css')) + installed_chtml = env.ChunkedInstall( + env.Dir(htmldir), + os.path.join(build, doc, f'scons-{doc}', 'index.html'), + ) + installed_chtml_css = env.Install( + env.Dir(htmldir), os.path.join(build, doc, 'scons.css') + ) env.Depends(installed_chtml, docs[doc][DOCNODES]) env.Depends(installed_chtml_css, docs[doc][DOCNODES]) @@ -447,7 +499,10 @@ else: env.Ignore(htmlindex, version_xml) if 'html' in docs[doc][DOCTARGETS]: - env.InstallAs(env.File(html), env.File(os.path.join(build, doc, 'index.html'))) + env.InstallAs( + target=env.File(html), + source=env.File(os.path.join(build, doc, 'index.html')), + ) tar_deps.extend([html]) tar_list.extend([html]) Local(html) @@ -455,7 +510,10 @@ else: install_css = True if 'pdf' in docs[doc][DOCTARGETS]: - env.InstallAs(env.File(pdf), env.File(os.path.join(build, doc, 'scons-%s.pdf' % doc))) + env.InstallAs( + target=env.File(pdf), + source=env.File(os.path.join(build, doc, f'scons-{doc}.pdf')), + ) Local(pdf) env.Ignore(pdf, version_xml) @@ -463,21 +521,31 @@ else: tar_list.append(pdf) if 'epub' in docs[doc][DOCTARGETS] and gs: - env.InstallAs(env.File(epub), env.File(os.path.join(build, doc, 'scons-%s.epub' % doc))) + env.InstallAs( + target=env.File(epub), + source=env.File(os.path.join(build, doc, f'scons-{doc}.epub')), + ) Local(epub) env.Ignore(epub, version_xml) tar_deps.append(epub) tar_list.append(epub) - if ('text' in docs[doc][DOCTARGETS] and lynx and - (('html' in docs[doc][DOCTARGETS]) or (doc == 'man'))): + if ( + 'text' in docs[doc][DOCTARGETS] + and lynx + and (('html' in docs[doc][DOCTARGETS]) or (doc == 'man')) + ): texthtml = os.path.join(build, doc, 'index.html') if doc == 'man': # Special handling for single MAN file texthtml = os.path.join(build, doc, 'scons-scons.html') - env.Command(text, env.File(texthtml), "lynx -dump ${SOURCE.abspath} > $TARGET") + env.Command( + target=text, + source=env.File(texthtml), + action="lynx -dump ${SOURCE.abspath} > $TARGET", + ) Local(text) env.Ignore(text, version_xml) @@ -486,17 +554,20 @@ else: tar_list.append(text) if 'man' in docs[doc][DOCTARGETS]: - # - # Man page(s) - # for m in man_page_list: man, _1 = os.path.splitext(m) - pdf = os.path.join(build, 'PDF', '%s-man.pdf' % man) - html = os.path.join(build, 'HTML', '%s-man.html' % man) + pdf = os.path.join(build, 'PDF', f'{man}-man.pdf') + html = os.path.join(build, 'HTML', f'{man}-man.html') - env.InstallAs(env.File(pdf), env.File(os.path.join(build, 'man', 'scons-%s.pdf' % man))) - env.InstallAs(env.File(html), env.File(os.path.join(build, 'man', 'scons-%s.html' % man))) + env.InstallAs( + target=env.File(pdf), + source=env.File(os.path.join(build, 'man', f'scons-{man}.pdf')), + ) + env.InstallAs( + target=env.File(html), + source=env.File(os.path.join(build, 'man', f'scons-{man}.html')), + ) tar_deps.extend([pdf, html]) tar_list.extend([pdf, html]) @@ -504,8 +575,10 @@ else: # Install CSS file, common to all single HTMLs if install_css: css_file = os.path.join(build, 'HTML', 'scons.css') - env.InstallAs(env.File(css_file), - env.File(os.path.join(build, 'user', 'scons.css'))) + env.InstallAs( + target=env.File(css_file), + source=env.File(os.path.join(build, 'user', 'scons.css')), + ) tar_deps.extend([css_file]) tar_list.extend([css_file]) Local(css_file) @@ -513,20 +586,23 @@ else: if not skip_doc: # Build API DOCS # TODO: Better specify dependencies on source files - pdf_file = env.Command('#/build/doc/api/scons-api.pdf', - env.Glob('#/SCons/*'), - [Delete("#/build/doc/api"), - "cd doc && make pdf"]) + pdf_file = env.Command( + target='#/build/doc/api/scons-api.pdf', + source=env.Glob('#/SCons/*'), + action=[Delete("#/build/doc/api"), "cd doc && make pdf"], + ) pdf_install = os.path.join(build, 'PDF', 'scons-api.pdf') - env.InstallAs(pdf_install, pdf_file) + env.InstallAs(target=pdf_install, source=pdf_file) tar_deps.append(pdf_install) tar_list.append(pdf_install) htmldir = os.path.join(build, 'HTML', 'scons-api') - html_files = env.Command('#/build/doc/HTML/scons-api/index.html', - env.Glob('#/SCons/*'), - "cd doc && make dirhtml BUILDDIR=${HTMLDIR}", - HTMLDIR=htmldir) + html_files = env.Command( + target='#/build/doc/HTML/scons-api/index.html', + source=env.Glob('#/SCons/*'), + action="cd doc && make dirhtml BUILDDIR=${HTMLDIR}", + HTMLDIR=htmldir, + ) tar_deps.append(htmldir) tar_list.append(htmldir) @@ -536,8 +612,11 @@ if not skip_doc: # if tar_deps: tar_list = ' '.join([x.replace(build + '/', '') for x in tar_list]) - t = env.Command(dist_doc_tar_gz, tar_deps, - "tar cf${TAR_HFLAG} - -C %s %s | gzip > $TARGET" % (build, tar_list)) + t = env.Command( + target=dist_doc_tar_gz, + source=tar_deps, + action="tar cf${TAR_HFLAG} - -C %s %s | gzip > $TARGET" % (build, tar_list), + ) AddPostAction(dist_doc_tar_gz, Chmod(dist_doc_tar_gz, 0o644)) Local(t) Alias('doc', t) diff --git a/site_scons/BuildCommandLine.py b/site_scons/BuildCommandLine.py index 14bc279..d401aed 100644 --- a/site_scons/BuildCommandLine.py +++ b/site_scons/BuildCommandLine.py @@ -1,57 +1,84 @@ -import time +# SPDX-License-Identifier: MIT +# +# Copyright The SCons Foundation + +"""Command-line processing for building SCons.""" + import os -import socket +import platform +import time +import SCons.Errors from SCons.Script import ARGUMENTS + class BuildCommandLine: git = None def init_command_line_variables(self): self.command_line_variables = [ - ("BUILDDIR=", "The directory in which to build the packages. " + - "The default is the './build' subdirectory."), - - ("BUILD_ID=", "An identifier for the specific build." + - "The default is the Subversion revision number."), - - ("BUILD_SYSTEM=", "The system on which the packages were built. " + - "The default is whatever hostname is returned " + - "by socket.gethostname(). If SOURCE_DATE_EPOCH " + - "env var is set, '_reproducible' is the default."), - - ("CHECKPOINT=", "The specific checkpoint release being packaged, " + - "which will be appended to the VERSION string. " + - "A value of CHECKPOINT=d will generate a string " + - "of 'd' plus today's date in the format YYYMMDD. " + - "A value of CHECKPOINT=r will generate a " + - "string of 'r' plus the Subversion revision " + - "number. Any other CHECKPOINT= string will be " + - "used as is. There is no default value."), - - ("DATE=", "The date string representing when the packaging " + - "build occurred. The default is the day and time " + - "the SConstruct file was invoked, in the format " + - "YYYY/MM/DD HH:MM:SS."), - - ("DEVELOPER=", "The developer who created the packages. " + - "The default is the first set environment " + - "variable from the list $USERNAME, $LOGNAME, $USER." + - "If the SOURCE_DATE_EPOCH env var is set, " + - "'_reproducible' is the default."), - - ("REVISION=", "The revision number of the source being built. " + - "The default is the git hash returned " + - "'git rev-parse HEAD', with an appended string of " + - "'[MODIFIED]' if there are any changes in the " + - "working copy."), - - ("VERSION=", "The SCons version being packaged. The default " + - "is the hard-coded value '%s' " % self.default_version + - "from this SConstruct file."), - - ("SKIP_DOC=", "Skip building all documents. The default is False (build docs)"), + ( + "BUILDDIR=", + "The directory to build the packages in. " + "The default is './build'." + ), + ( + "BUILD_ID=", + "An identifier for the specific build. " + "The default is to generate an id from 'git rev-parse'." + ), + ( + "BUILD_SYSTEM=", + "The system on which the packages were built. " + "The default is the inspected hostname. " + "If env var SOURCE_DATE_EPOCH is set, " + "defaults to '_reproducible'." + ), + ( + "CHECKPOINT=", + "Indicates a checkpoint release, " + "which will be appended to the VERSION string. " + "A value of 'd' selects a date checkpoint " + "(a string of 'd' plus today's date in the format YYYMMDD). " + "A value of 'r' selects a revision checkpoint " + "(string of 'r' plus the revision number). " + "Any other value will be used as is. There is no default." + ), + ( + "DATE=", + "The date string representing when the packaging " + "build occurred. The default is the day and time " + "the SConstruct file was invoked, in the format " + "YYYY/MM/DD HH:MM:SS. If env var SOURCE_DATE_EPOCH is set, " + "its value (must be a UNIX-style timestamp) is used." + ), + ( + "DEVELOPER=", + "The developer who created the packages. " + "The default is the first set environment " + "variable from the list $USERNAME, $LOGNAME, $USER." + "If env var SOURCE_DATE_EPOCH is set, " + "defaults to '_reproducible'." + ), + ( + "REVISION=", + "The revision number of the source being built. " + "The default is the git hash returned " + "'git rev-parse HEAD', with an appended string of " + "'[MODIFIED]' if there are any changes in the " + "working copy." + ), + ( + "VERSION=", + "The SCons version being packaged. The default " + f"is the hard-coded value '{self.default_version}' " + "from this SConstruct file." + ), + ( + "SKIP_DOC=", + "Skip building all documents. The default is False (build docs)" + ), ] def __init__(self, default_version="99.99.99"): @@ -77,8 +104,32 @@ class BuildCommandLine: min = (time.daylight and time.altzone or time.timezone) // 60 hr = min // 60 min = -(min % 60 + hr * 100) - self.date = (time.strftime('%a, %d %b %Y %X', time.localtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))) - + ' %+.4d' % min) + # TODO: is it better to take the date of last rev? Externally: + # SOURCE_DATE_EPOCH =`git log -1 --pretty=%ct` + self.date = ( + time.strftime( + '%a, %d %b %Y %X', + time.localtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))), + ) + + ' %+.4d' % min + ) + # Alternate proposal: + # from datetime import datetime, timezone + # timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', time.time())) + # try: + # dt = datetime.fromtimestamp(timestamp, timezone.utc) + # except (OverflowError, OSError): + # # SOURCE_DATE_EPOCH spec: If the value is malformed, + # # the build process SHOULD exit with a non-zero error code. + # # Python: This may raise OverflowError, if the timestamp is out + # # of the range of values supported by the platform C gmtime() + # # function, and OSError on gmtime() failure. It’s common for + # # this to be restricted to years in 1970 through 2038. + # raise SCons.Errors.UserError( + # "Invalid value for SOURCE_DATE_EPOCH environment var, " + # "please correct to a valid timestamp" + # ) + # self.date = dt.strftime("%a, %d %b %Y %X %Z") # or %z for numeric def process_command_line_vars(self): # @@ -102,14 +153,14 @@ class BuildCommandLine: if os.environ.get('SOURCE_DATE_EPOCH'): self.build_system = '_reproducible' else: - self.build_system = socket.gethostname().split('.')[0] + self.build_system = platform.node().split('.')[0] self.version = ARGUMENTS.get('VERSION', '') if not self.version: self.version = self.default_version if BuildCommandLine.git: - cmd = "%s ls-files 2> /dev/null" % BuildCommandLine.git + cmd = f"{BuildCommandLine.git} ls-files 2> /dev/null" with os.popen(cmd, "r") as p: self.git_status_lines = p.readlines() @@ -118,10 +169,12 @@ class BuildCommandLine: def _generate_build_id(revision): return revision - generate_build_id=_generate_build_id + generate_build_id = _generate_build_id if not self.revision and BuildCommandLine.git: - with os.popen("%s rev-parse HEAD 2> /dev/null" % BuildCommandLine.git, "r") as p: + with os.popen( + f"{BuildCommandLine.git} rev-parse HEAD 2> /dev/null", "r" + ) as p: self.git_hash = p.read().strip() def _generate_build_id_git(revision): diff --git a/site_scons/SConsRevision.py b/site_scons/SConsRevision.py index f6f7a71..dab623c 100644 --- a/site_scons/SConsRevision.py +++ b/site_scons/SConsRevision.py @@ -1,3 +1,7 @@ +# SPDX-License-Identifier: MIT +# +# Copyright The SCons Foundation + import os def SCons_revision(target, source, env): @@ -29,7 +33,7 @@ def SCons_revision(target, source, env): contents = contents.replace('__REVISION' + '__', env['REVISION']) contents = contents.replace('__VERSION' + '__', env['VERSION']) contents = contents.replace('__NULL' + '__', '') - + with open(t,'w') as of: of.write(contents) except UnicodeDecodeError as e: @@ -39,4 +43,4 @@ def SCons_revision(target, source, env): of.write(contents) - os.chmod(t, os.stat(s)[0]) \ No newline at end of file + os.chmod(t, os.stat(s)[0]) diff --git a/site_scons/Utilities.py b/site_scons/Utilities.py index ebc7bce..7ef45a3 100644 --- a/site_scons/Utilities.py +++ b/site_scons/Utilities.py @@ -1,17 +1,17 @@ +# SPDX-License-Identifier: MIT +# +# Copyright The SCons Foundation + import os import stat import time import sysconfig - platform = sysconfig.get_platform() -def is_windows(): - """ Check if we're on a Windows platform""" - if platform.startswith('win'): - return True - else: - return False +def is_windows() -> bool: + """Check if we're on a Windows platform""" + return platform.startswith('win') def whereis(filename): @@ -35,9 +35,7 @@ def whereis(filename): return f_ext return None + # Datestring for debian # Should look like: Mon, 03 Nov 2016 13:37:42 -0700 deb_date = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) - - - diff --git a/site_scons/epydoc.py b/site_scons/epydoc.py index 3d6e5de..7b553fe 100644 --- a/site_scons/epydoc.py +++ b/site_scons/epydoc.py @@ -1,29 +1,13 @@ +# SPDX-License-Identifier: MIT # -# Setup epydoc builder -# +# Copyright The SCons Foundation + +""" +Setup epydoc builder + +Obsolete: epydoc no longer maintained. +""" -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# from Utilities import whereis from SCons.Script import Delete, Touch, WhereIs epydoc_cli = WhereIs('epydoc') diff --git a/site_scons/scons_local_package.py b/site_scons/scons_local_package.py index 8eca758..a9f391b 100644 --- a/site_scons/scons_local_package.py +++ b/site_scons/scons_local_package.py @@ -1,25 +1,8 @@ -# MIT License +# SPDX-License-Identifier: MIT # # Copyright The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +"""Build the scons-local packages.""" from glob import glob import os.path @@ -93,29 +76,29 @@ def create_local_packages(env): installed_files = install_local_package_files(env) build_local_dir = 'build/scons-local' - package = env.Command( + local_zip = env.Command( '#build/dist/scons-local-${VERSION}.zip', installed_files, zipit, CD=build_local_dir, PSV='.', ) - - do_zipapp = True # Q: maybe an external way to specify whether to build? - if do_zipapp: - # We need to descend into the versioned directory for zipapp, - # but we don't know the version. env.Glob lets us expand that. - # The action isn't going to use the sources, but including - # them makes sure SCons has populated the dir we're going to zip. - app_dir = env.Glob(f"{build_local_dir}/scons-local-*")[0] - zipapp = env.Command( - target='#build/dist/scons-local-${VERSION}.pyz', - source=installed_files, - action=zipappit, - CD=app_dir, - PSV='.', - entry='SCons.Script.Main:main', - ) + env.Alias("local-zip", local_zip) + + # We need to descend into the versioned directory for zipapp, + # but we don't know the version. env.Glob lets us expand that. + # The action isn't going to use the sources, but including + # them makes sure SCons has populated the dir we're going to zip. + app_dir = env.Glob(f"{build_local_dir}/scons-local-*")[0] + zipapp = env.Command( + target='#build/dist/scons-local-${VERSION}.pyz', + source=installed_files, + action=zipappit, + CD=app_dir, + PSV='.', + entry='SCons.Script.Main:main', + ) + env.Alias("local-zipapp", zipapp) if is_windows(): # avoid problem with tar interpreting c:/ as a remote machine @@ -123,12 +106,12 @@ def create_local_packages(env): else: tar_cargs = '-czf' - env.Command( + local_tar = env.Command( '#build/dist/scons-local-${VERSION}.tar.gz', installed_files, "cd %s && tar %s $( ${TARGET.abspath} $) *" % (build_local_dir, tar_cargs), ) + env.Alias("local-tar-gz", local_tar) - print(f"Package:{package}") - if do_zipapp: - print(f"Zipapp:{zipapp}") + print(f"Package:{local_zip}") + print(f"Zipapp:{zipapp}") diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 7d51fda..f87ca60 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -1,3 +1,7 @@ +# SPDX-License-Identifier: MIT +# +# Copyright The SCons Foundation + # flake8: noqa import os.path diff --git a/site_scons/soe_utils.py b/site_scons/soe_utils.py index dfd605c..8fb0257 100644 --- a/site_scons/soe_utils.py +++ b/site_scons/soe_utils.py @@ -1,8 +1,13 @@ +# SPDX-License-Identifier: MIT +# +# Copyright The SCons Foundation + import os.path import re from SCons.Script import Builder, Action, Scanner + def soelim(target, source, env): """ Interpolate files included in [gnt]roff source files using the @@ -26,9 +31,10 @@ def soelim(target, source, env): else: tfp.write(line) + def soscan(node, env, path): c = node.get_text_contents() return re.compile(r"^[.']so\s+(\S+)", re.M).findall(c) -soelimbuilder = Builder(action = Action(soelim), - source_scanner = Scanner(soscan)) + +soelimbuilder = Builder(action=Action(soelim), source_scanner=Scanner(soscan)) diff --git a/site_scons/update_build_info.py b/site_scons/update_build_info.py index 59ddf0c..51b3914 100644 --- a/site_scons/update_build_info.py +++ b/site_scons/update_build_info.py @@ -1,3 +1,8 @@ +# SPDX-License-Identifier: MIT +# +# Copyright The SCons Foundation + + def update_init_file(env): substitutions = { '__version__': env['VERSION'], @@ -8,10 +13,13 @@ def update_init_file(env): "__revision__": env['REVISION'], "__build__": env['BUILD'], } - si = env.Textfile('#SCons/__init__.py', - ["%s=\"%s\"" % (k, v) for k, v in substitutions.items()] + - ['# make sure compatibility is always in place', - 'import SCons.compat # noqa'], - ) + si = env.Textfile( + '#SCons/__init__.py', + ["%s=\"%s\"" % (k, v) for k, v in substitutions.items()] + + [ + '# make sure compatibility is always in place', + 'import SCons.compat # noqa', + ], + ) env.Precious(si) env.NoClean(si) # Don't clean this file as it breaks the build. diff --git a/site_scons/zip_utils.py b/site_scons/zip_utils.py index a38a68f..90fd153 100644 --- a/site_scons/zip_utils.py +++ b/site_scons/zip_utils.py @@ -1,29 +1,10 @@ -# MIT License +# SPDX-License-Identifier: MIT # # Copyright The SCons Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -Actions to zip and unzip, for working with SCons release bundles -Action for creating a zipapp +Actions to zip and unzip, for working with SCons release bundles. +Action for creating a zipapp. """ import os.path -- cgit v0.12