#!/usr/bin/env python # Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import third_party from util import build_mode, build_path, enable_ansi_colors, root_path, run from util import shell_quote, run_output import os import re import sys from distutils.spawn import find_executable import prebuilt import argparse parser = argparse.ArgumentParser() parser.add_argument( "--no-binary-download", help="Do not download binaries, must use depot_tools manually", action="store_true") def main(): enable_ansi_colors() os.chdir(root_path) args = parser.parse_args() if args.no_binary_download: print "no binary download" else: print "binary download" third_party.download_gn() third_party.download_clang_format() third_party.download_clang() third_party.maybe_download_sysroot() write_lastchange() mode = build_mode(default=None) if mode is not None: gn_gen(mode) else: gn_gen("release") gn_gen("debug") def write_if_not_exists(filename, contents): if not os.path.exists(filename): with open(filename, "w+") as f: f.write(contents) def write_lastchange(): write_if_not_exists( "build/util/LASTCHANGE", "LASTCHANGE=c42e4ddbb7973bfb0c57a49ab6bf6dc432baad7e-\n") write_if_not_exists("build/util/LASTCHANGE.committime", "1535518087") # TODO Properly we should call the following script, but it seems to cause # a rebuild on every commit. # run([ # sys.executable, "build/util/lastchange.py", "-o", # "build/util/LASTCHANGE", "--source-dir", root_path, "--filter=" # ]) # If this text is found in args.gn, we assume it hasn't been hand edited. gn_args_header = [ "# This file is automatically generated by tools/setup.py.", "# REMOVE THIS LINE to preserve any changes you make.", "" ] def gn_string(s): # In gn, strings are enclosed in double-quotes and use backslash as the # escape character. The only escape sequences supported are: # \" (for literal quote) # \$ (for literal dollars sign) # \\ (for literal backslash) # Any other use of a backslash is treated as a literal backslash. s = re.sub(r'("|\$|\\(?=["$\\]))', r'\\\1', s) s = '"' + s + '"' return s def gn_args_are_generated(lines): for line in lines: if re.match("^\s*#.*REMOVE THIS LINE", line): return True return False def read_gn_args(args_filename): if not os.path.exists(args_filename): return (None, False) # No content, not hand edited. with open(args_filename) as f: lines = f.read().splitlines() args = [l.strip() for l in lines if not re.match("^\s*(#|$)", l)] hand_edited = not gn_args_are_generated(lines) return (args, hand_edited) def write_gn_args(args_filename, args): assert not gn_args_are_generated(args) # No header -> hand crafted. lines = gn_args_header + args assert gn_args_are_generated(lines) # With header -> generated. # Ensure the directory where args.gn goes exists. d = os.path.dirname(args_filename) if not os.path.isdir(d): os.makedirs(d) with open(args_filename, "w") as f: f.write("\n".join(lines) + "\n") def generate_gn_args(mode): out = [] if mode == "release": out += ["is_official_build=true"] elif mode == "debug": out += ["is_debug=true"] else: print "Bad mode {}. Use 'release' or 'debug' (default)" % mode sys.exit(1) if "DENO_BUILD_ARGS" in os.environ: out += os.environ["DENO_BUILD_ARGS"].split() cacher = prebuilt.load_sccache() if not os.path.exists(cacher): cacher = find_executable("sccache") or find_executable("ccache") # Check if ccache or sccache are in the path, and if so we set cc_wrapper. cc_wrapper = cacher if cc_wrapper: # The gn toolchain does not shell escape cc_wrapper, so do it here. out += ['cc_wrapper=%s' % gn_string(shell_quote(cc_wrapper))] if os.name == "nt": # Disable treat_warnings_as_errors until this sccache bug is fixed: # https://github.com/mozilla/sccache/issues/264 out += ["treat_warnings_as_errors=false"] # Look for sccache; if found, set rustc_wrapper. rustc_wrapper = cacher if rustc_wrapper: out += ['rustc_wrapper=%s' % gn_string(rustc_wrapper)] return out # gn gen. def gn_gen(mode): os.environ["DENO_BUILD_MODE"] = mode # Rather than using gn gen --args we write directly to the args.gn file. # This is to avoid quoting/escaping complications when passing overrides as # command-line arguments. args_filename = os.path.join(build_path(), "args.gn") # Check if args.gn exists, and if it was auto-generated or handcrafted. existing_gn_args, hand_edited = read_gn_args(args_filename) # If args.gn wasn't handcrafted, regenerate it. if hand_edited: print "%s: Using gn options from hand edited '%s'." % (mode, args_filename) gn_args = existing_gn_args else: print "%s: Writing gn options to '%s'." % (mode, args_filename) gn_args = generate_gn_args(mode) if gn_args != existing_gn_args: write_gn_args(args_filename, gn_args) for line in gn_args: print " " + line run([third_party.gn_path, "gen", build_path()], env=third_party.google_env()) if __name__ == '__main__': sys.exit(main())