Commit 75bb49ad authored by Sylvain Defresne's avatar Sylvain Defresne Committed by Commit Bot

[ios] Fix interaction of build/clobber.py & ios/build/tools/setup-gn.py

The script build/clobber.py tried to parse the build.ninja when
clobbering a directory and use it as a template, however if the
parsing failed, it generated a broken build.ninja file.

The script ios/build/tools/setup-gn.py would create multiple
empty build directory with just args.gn and a build.ninja file
that would result in invoking `gn gen` as a first step.

However, the build.ninja created by setup-gn.py could not be
parsed by clobber.py since it did not include minimum required
version of ninja command. Also, setup-gn.py did not recreate
the file if they existed, even if they were incorrect.

All this caused build/clobber.py to sometimes leave a build
directory in an unbuildable state requiring the developer to
run `gn gen` manually.

To fix this, change setup-gn.py to always overwrite build.ninja
file, to use a format that can be parsed by clobber.py and fix
clobber.py to write a build.ninja file that is valid if parsing
fails.

Always overwrite the build.ninja file to ensure that `--check`
is added to `gn gen` invocation (as locally running `gn gen`
without `--check` even once would disable the local check of the
deps and later be reported by Chrome iOS developers as a bug).

Bug: 1102331
Change-Id: Iabe906ac5f676f1b837c6ebb03ee32d73c7b6cf9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2283143
Auto-Submit: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: default avatarDirk Pranke <dpranke@google.com>
Reviewed-by: default avatarOlivier Robin <olivierrobin@chromium.org>
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
Cr-Commit-Position: refs/heads/master@{#786208}
parent 17fcf703
......@@ -89,13 +89,15 @@ def delete_build_dir(build_dir):
f.write(build_commands)
else:
# Couldn't parse the build.ninja file, write a default thing.
f.write('''rule gn
command = gn -q gen //out/%s/
description = Regenerating ninja files
f.write('''ninja_required_version = 1.7.2
rule gn
command = gn -q gen //out/%s/
description = Regenerating ninja files
build build.ninja: gn
generator = 1
depfile = build.ninja.d
generator = 1
depfile = build.ninja.d
''' % (os.path.split(build_dir)[1]))
# Write a .d file for the build which references a nonexistant file. This
......
......@@ -126,83 +126,65 @@ class GnGenerator(object):
return args
def Generate(self, gn_path, root_path, out_path):
buf = io.StringIO()
self.WriteArgsGn(buf)
WriteToFileIfChanged(
os.path.join(out_path, 'args.gn'),
buf.getvalue(),
overwrite=True)
def Generate(self, gn_path, root_path, build_dir):
self.WriteArgsGn(build_dir)
subprocess.check_call(
self.GetGnCommand(gn_path, root_path, out_path, True))
def CreateGnRules(self, gn_path, root_path, out_path):
buf = io.StringIO()
self.WriteArgsGn(buf)
WriteToFileIfChanged(
os.path.join(out_path, 'args.gn'),
buf.getvalue(),
overwrite=True)
buf = io.StringIO()
gn_command = self.GetGnCommand(gn_path, root_path, out_path, False)
self.WriteBuildNinja(buf, gn_command)
WriteToFileIfChanged(
os.path.join(out_path, 'build.ninja'),
buf.getvalue(),
overwrite=False)
buf = io.StringIO()
self.WriteBuildNinjaDeps(buf)
WriteToFileIfChanged(
os.path.join(out_path, 'build.ninja.d'),
buf.getvalue(),
overwrite=False)
def WriteArgsGn(self, stream):
stream.write('# This file was generated by setup-gn.py. Do not edit\n')
stream.write('# but instead use ~/.setup-gn or $repo/.setup-gn files\n')
stream.write('# to configure settings.\n')
stream.write('\n')
if self._settings.has_section('$imports$'):
for import_rule in self._settings.values('$imports$'):
stream.write('import("%s")\n' % import_rule)
self.GetGnCommand(gn_path, root_path, build_dir, True))
def CreateGnRules(self, gn_path, root_path, build_dir):
gn_command = self.GetGnCommand('gn', root_path, build_dir, False)
self.WriteArgsGn(build_dir)
self.WriteBuildNinja(gn_command, build_dir)
self.WriteBuildNinjaDeps(build_dir)
def WriteArgsGn(self, build_dir):
with open(os.path.join(build_dir, 'args.gn'), 'w') as stream:
stream.write('# This file was generated by setup-gn.py. Do not edit\n')
stream.write('# but instead use ~/.setup-gn or $repo/.setup-gn files\n')
stream.write('# to configure settings.\n')
stream.write('\n')
gn_args = self._GetGnArgs()
for name, value in gn_args:
if isinstance(value, bool):
stream.write('%s = %s\n' % (name, str(value).lower()))
elif isinstance(value, list):
stream.write('%s = [%s' % (name, '\n' if len(value) > 1 else ''))
if len(value) == 1:
prefix = ' '
suffix = ' '
else:
prefix = ' '
suffix = ',\n'
for item in value:
if isinstance(item, bool):
stream.write('%s%s%s' % (prefix, str(item).lower(), suffix))
if self._settings.has_section('$imports$'):
for import_rule in self._settings.values('$imports$'):
stream.write('import("%s")\n' % import_rule)
stream.write('\n')
gn_args = self._GetGnArgs()
for name, value in gn_args:
if isinstance(value, bool):
stream.write('%s = %s\n' % (name, str(value).lower()))
elif isinstance(value, list):
stream.write('%s = [%s' % (name, '\n' if len(value) > 1 else ''))
if len(value) == 1:
prefix = ' '
suffix = ' '
else:
stream.write('%s%s%s' % (prefix, item, suffix))
stream.write(']\n')
else:
stream.write('%s = %s\n' % (name, value))
def WriteBuildNinja(self, stream, gn_command):
stream.write('rule gn\n')
stream.write(' command = %s\n' % NinjaEscapeCommand(gn_command))
stream.write(' description = Regenerating ninja files\n')
stream.write('\n')
stream.write('build build.ninja: gn\n')
stream.write(' generator = 1\n')
stream.write(' depfile = build.ninja.d\n')
def WriteBuildNinjaDeps(self, stream):
stream.write('build.ninja: nonexistant_file.gn\n')
prefix = ' '
suffix = ',\n'
for item in value:
if isinstance(item, bool):
stream.write('%s%s%s' % (prefix, str(item).lower(), suffix))
else:
stream.write('%s%s%s' % (prefix, item, suffix))
stream.write(']\n')
else:
stream.write('%s = %s\n' % (name, value))
def WriteBuildNinja(self, gn_command, build_dir):
with open(os.path.join(build_dir, 'build.ninja'), 'w') as stream:
stream.write('ninja_required_version = 1.7.2\n')
stream.write('\n')
stream.write('rule gn\n')
stream.write(' command = %s\n' % NinjaEscapeCommand(gn_command))
stream.write(' description = Regenerating ninja files\n')
stream.write('\n')
stream.write('build build.ninja: gn\n')
stream.write(' generator = 1\n')
stream.write(' depfile = build.ninja.d\n')
def WriteBuildNinjaDeps(self, build_dir):
with open(os.path.join(build_dir, 'build.ninja.d'), 'w') as stream:
stream.write('build.ninja: nonexistant_file.gn\n')
def GetGnCommand(self, gn_path, src_path, out_path, generate_xcode_project):
gn_command = [ gn_path, '--root=%s' % os.path.realpath(src_path), '-q' ]
......@@ -223,21 +205,6 @@ class GnGenerator(object):
return gn_command
def WriteToFileIfChanged(filename, content, overwrite):
'''Write |content| to |filename| if different. If |overwrite| is False
and the file already exists it is left untouched.'''
if os.path.exists(filename):
if not overwrite:
return
with open(filename) as file:
if file.read() == content:
return
if not os.path.isdir(os.path.dirname(filename)):
os.makedirs(os.path.dirname(filename))
with open(filename, 'w') as file:
file.write(content)
def NinjaNeedEscape(arg):
'''Returns True if |arg| needs to be escaped when written to .ninja file.'''
return ':' in arg or '*' in arg or ';' in arg
......@@ -289,6 +256,9 @@ def GenerateGnBuildRules(gn_path, root_dir, out_dir, settings):
for config in SUPPORTED_CONFIGS:
for target in SUPPORTED_TARGETS:
build_dir = os.path.join(out_dir, '%s-%s' % (config, target))
if not os.path.isdir(build_dir):
os.makedirs(build_dir)
generator = GnGenerator(settings, config, target)
generator.CreateGnRules(gn_path, root_dir, build_dir)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment