From 9b052ace09a47f14cd59eaf8734a5e6cb1b7e5b8 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Sat, 8 Sep 2018 17:56:19 +0200 Subject: [PATCH] tools/setup: replace generated args.gn unless handcrafted --- tools/setup.py | 78 +++++++++++++++++++++++++++++++++++---------- tools/setup_test.py | 61 +++++++++++++++++++++++++++++++++++ tools/test.py | 2 ++ 3 files changed, 125 insertions(+), 16 deletions(-) create mode 100644 tools/setup_test.py diff --git a/tools/setup.py b/tools/setup.py index 737f80017f..77aaf95d60 100755 --- a/tools/setup.py +++ b/tools/setup.py @@ -2,6 +2,7 @@ import third_party from util import run, root_path, build_path, build_mode import os +import re import sys from distutils.spawn import find_executable @@ -44,15 +45,55 @@ def write_lastchange(): # ]) -def get_gn_args(): +# 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_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. + dir = os.path.dirname(args_filename) + if not os.path.isdir(dir): + os.makedirs(dir) + + with open(args_filename, "w") as f: + f.write("\n".join(lines) + "\n") + + +def generate_gn_args(mode): out = [] - if build_mode() == "release": + if mode == "release": out += ["is_official_build=true"] - elif build_mode() == "debug": + elif mode == "debug": pass else: - print "Bad mode {}. Use 'release' or 'debug' (default)" % build_mode() + 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() @@ -67,8 +108,6 @@ def get_gn_args(): tc = "//build_extra/toolchain/win:win_clang_x64" out += ['custom_toolchain="%s"' % tc, 'host_toolchain="%s"' % tc] - print "DENO_BUILD_ARGS:", out - return out @@ -76,20 +115,27 @@ def get_gn_args(): def gn_gen(mode): os.environ["DENO_BUILD_MODE"] = mode - # mkdir $build_path(). We do this so we can write args.gn before running gn gen. - if not os.path.isdir(build_path()): - os.makedirs(build_path()) - - # Rather than using gn gen --args we manually write the args.gn override file. + # 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") - if os.path.exists(args_filename): - print "Skip writing args file: '%s' already exists." % args_filename + + # 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: - gn_args = get_gn_args() - with open(args_filename, "w+") as f: - f.write("\n".join(gn_args) + "\n") + 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()) diff --git a/tools/setup_test.py b/tools/setup_test.py new file mode 100644 index 0000000000..670bf33dba --- /dev/null +++ b/tools/setup_test.py @@ -0,0 +1,61 @@ +# Copyright 2018 the Deno authors. All rights reserved. MIT license. + +import os +from setup import read_gn_args, write_gn_args +from shutil import rmtree +from tempfile import mktemp + + +def read_gn_args_test(): + # Args file doesn't exist. + (args, hand_edited) = read_gn_args("/baddir/hopefully/nonexistent/args.gn") + assert args is None + assert hand_edited == False + + # Handwritten empty args file. + filename = mktemp() + with open(filename, "w"): + pass + (args, hand_edited) = read_gn_args(filename) + os.remove(filename) + assert args == [] + assert hand_edited == True + + # Handwritten non-empty args file. + expect_args = ['some_number=2', 'another_string="ran/dom#yes"'] + filename = mktemp() + with open(filename, "w") as f: + f.write("\n".join(expect_args + ["", "# A comment to be ignored"])) + (args, hand_edited) = read_gn_args(filename) + os.remove(filename) + assert args == expect_args + assert hand_edited == True + + +def write_gn_args_test(): + # Build a nonexistent path; write_gn_args() should call mkdir as needed. + dir = mktemp() + filename = os.path.join(dir, "args.gn") + assert not os.path.exists(dir) + assert not os.path.exists(filename) + # Write some args. + args = ['lalala=42', 'foo_bar_baz="lorem ipsum dolor#amet"'] + write_gn_args(filename, args) + # Directory and args file should now be created. + assert os.path.isdir(dir) + assert os.path.isfile(filename) + # Validate that the right contents were written. + (check_args, hand_edited) = read_gn_args(filename) + assert check_args == args + assert hand_edited == False + # Clean up. + rmtree(dir) + + +def setup_test(): + read_gn_args_test() + write_gn_args_test() + + +if __name__ == '__main__': + setup_test() diff --git a/tools/test.py b/tools/test.py index 6e5cb548b9..b8f49cf2b2 100755 --- a/tools/test.py +++ b/tools/test.py @@ -4,6 +4,7 @@ import os import sys from check_output_test import check_output_test +from setup_test import setup_test from util import executable_suffix, run, build_path from unit_tests import unit_tests from util_test import util_test @@ -30,6 +31,7 @@ def main(argv): http_server.spawn() # Internal tools testing + setup_test() util_test() test_cc = os.path.join(build_dir, "test_cc" + executable_suffix)