gn_helpers: Add support for imported args files.

The libassistant build calls gn_helpers to read the out dir's args.gn
file (while it's being invoked by ninja, mind you!) and passes its
contents through gn_helpers, applies a filtering, then creates a sub
build dir (within the original build dir) using its modified set of GN
args.

This is problematic since crbug.com/937821 is trying to transition
CrOS bots to using import() lines in their args file, which breaks
libassistant's build.

This fixes that by adding import support to gn_helpers.

Bug: 937821
Change-Id: I790bc27368611f31b63a0135c0e5919f35b21e6e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2107709
Reviewed-by: Takuto Ikuta <tikuta@chromium.org>
Commit-Queue: Ben Pastene <bpastene@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#752503}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 00156a211ee71ca14e6dcb1084b88b9ad92944e9
This commit is contained in:
Ben Pastene 2020-03-23 18:13:10 +00:00 коммит произвёл Commit Bot
Родитель 39e7b36992
Коммит d079f3a5d1
2 изменённых файлов: 89 добавлений и 1 удалений

Просмотреть файл

@ -19,9 +19,14 @@ To use in a random python file in the build:
Where the sequence of parameters to join is the relative path from your source
file to the build directory."""
import os
import re
import sys
IMPORT_RE = re.compile(r'^import\("//(\S+)"\)')
class GNException(Exception):
pass
@ -170,6 +175,31 @@ class GNValueParser(object):
def IsDone(self):
return self.cur == len(self.input)
def ReplaceImports(self):
"""Replaces import(...) lines with the contents of the imports.
Recurses on itself until there are no imports remaining, in the case of
nested imports.
"""
lines = self.input.splitlines()
if not any(line.startswith('import(') for line in lines):
return
for line in lines:
if not line.startswith('import('):
continue
regex_match = IMPORT_RE.match(line)
if not regex_match:
raise GNException('Not a valid import string: %s' % line)
import_path = os.path.join(
os.path.dirname(__file__), os.pardir, regex_match.group(1))
with open(import_path) as f:
imported_args = f.read()
self.input = self.input.replace(line, imported_args)
# Call ourselves again if we've just replaced an import() with additional
# imports.
self.ReplaceImports()
def ConsumeWhitespace(self):
while not self.IsDone() and self.input[self.cur] in ' \t\n':
self.cur += 1
@ -218,6 +248,7 @@ class GNValueParser(object):
"""
d = {}
self.ReplaceImports()
self.ConsumeWhitespace()
self.ConsumeComment()
while not self.IsDone():

Просмотреть файл

@ -2,9 +2,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import gn_helpers
import mock
import textwrap
import unittest
import gn_helpers
class UnitTest(unittest.TestCase):
def test_ToGNString(self):
self.assertEqual(
@ -122,5 +126,58 @@ class UnitTest(unittest.TestCase):
self.assertEqual(gn_helpers.FromGNArgs('foo_=true'),
{'foo_': True})
def test_ReplaceImports(self):
# Should be a no-op on args inputs without any imports.
parser = gn_helpers.GNValueParser(
textwrap.dedent("""
some_arg1 = "val1"
some_arg2 = "val2"
"""))
parser.ReplaceImports()
self.assertEquals(
parser.input,
textwrap.dedent("""
some_arg1 = "val1"
some_arg2 = "val2"
"""))
# A single "import(...)" line should be replaced with the contents of the
# file being imported.
parser = gn_helpers.GNValueParser(
textwrap.dedent("""
some_arg1 = "val1"
import("//some/args/file.gni")
some_arg2 = "val2"
"""))
fake_import = 'some_imported_arg = "imported_val"'
with mock.patch('__builtin__.open', mock.mock_open(read_data=fake_import)):
parser.ReplaceImports()
self.assertEquals(
parser.input,
textwrap.dedent("""
some_arg1 = "val1"
some_imported_arg = "imported_val"
some_arg2 = "val2"
"""))
# No trailing parenthesis should raise an exception.
with self.assertRaises(gn_helpers.GNException):
parser = gn_helpers.GNValueParser(
textwrap.dedent('import("//some/args/file.gni"'))
parser.ReplaceImports()
# No double quotes should raise an exception.
with self.assertRaises(gn_helpers.GNException):
parser = gn_helpers.GNValueParser(
textwrap.dedent('import(//some/args/file.gni)'))
parser.ReplaceImports()
# A path that's not source absolute should raise an exception.
with self.assertRaises(gn_helpers.GNException):
parser = gn_helpers.GNValueParser(
textwrap.dedent('import("some/relative/args/file.gni")'))
parser.ReplaceImports()
if __name__ == '__main__':
unittest.main()