summaryrefslogtreecommitdiff
path: root/tools/setup.py
blob: 77aaf95d600b2487aa0f2fdedef51f9cfc31d05a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env python
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


def main():
    os.chdir(root_path)

    third_party.fix_symlinks()
    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_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 mode == "release":
        out += ["is_official_build=true"]
    elif mode == "debug":
        pass
    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()

    # Check if ccache or sccache are in the path, and if so we set cc_wrapper.
    cc_wrapper = find_executable("ccache") or find_executable("sccache")
    if cc_wrapper:
        out += ['cc_wrapper="%s"' % cc_wrapper]
        # For cc_wrapper to work on Windows, we need to select our own toolchain
        # by overriding 'custom_toolchain' and 'host_toolchain'.
        # TODO: Is there a way to use it without the involvement of args.gn?
        if os.name == "nt":
            tc = "//build_extra/toolchain/win:win_clang_x64"
            out += ['custom_toolchain="%s"' % tc, 'host_toolchain="%s"' % tc]

    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())