From eb669cb02bb16154e0a3a8f3811f76a4c58853a6 Mon Sep 17 00:00:00 2001 From: Emanuele Giaquinta Date: Sun, 5 Jul 2020 09:24:49 +0300 Subject: [PATCH 1/2] compilation_db: test db regeneration on compile command change This commit adds a dummy '-g' option to the fake gcc so as to be able to modify a compile command by adding flags. --- test/CompilationDatabase/basic.py | 58 ++++++++++++++------- test/CompilationDatabase/fixture/SConstruct | 1 + test/fixture/mygcc.py | 2 +- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/test/CompilationDatabase/basic.py b/test/CompilationDatabase/basic.py index 2f7675276f..d86300a5c2 100644 --- a/test/CompilationDatabase/basic.py +++ b/test/CompilationDatabase/basic.py @@ -31,6 +31,27 @@ import os.path import TestSCons +def get_db(workdir, use_abspath, ccflags): + if use_abspath: + prefix = workdir + else: + prefix = '' + content = """[ + { + "command": "%s mygcc.py cc -o test_main.o %s test_main.c", + "directory": "%s", + "file": "%s", + "output": "%s" + } +]""" % (sys.executable, + ' '.join(['-c'] + ccflags), + workdir, + os.path.join(prefix, 'test_main.c'), + os.path.join(prefix, 'test_main.o')) + if sys.platform == 'win32': + content = content.replace('\\', '\\\\') + return content + test = TestSCons.TestSCons() test.file_fixture('mylink.py') @@ -56,35 +77,32 @@ 'compile_commands_over_abs_1.json', ] -example_rel_file = """[ - { - "command": "%s mygcc.py cc -o test_main.o -c test_main.c", - "directory": "%s", - "file": "test_main.c", - "output": "test_main.o" - } -]""" % (sys.executable, test.workdir) -if sys.platform == 'win32': - example_rel_file = example_rel_file.replace('\\', '\\\\') +example_rel_file = get_db(test.workdir, False, []) for f in rel_files: # print("Checking:%s" % f) test.must_exist(f) test.must_match(f, example_rel_file, mode='r') -example_abs_file = """[ - { - "command": "%s mygcc.py cc -o test_main.o -c test_main.c", - "directory": "%s", - "file": "%s", - "output": "%s" - } -]""" % (sys.executable, test.workdir, os.path.join(test.workdir, 'test_main.c'), os.path.join(test.workdir, 'test_main.o')) -if sys.platform == 'win32': - example_abs_file = example_abs_file.replace('\\', '\\\\') +example_abs_file = get_db(test.workdir, True, []) + +for f in abs_files: + test.must_exist(f) + test.must_match(f, example_abs_file, mode='r') + + +test.run(arguments='CCFLAGS=-g') + +example_rel_file = get_db(test.workdir, False, ['-g']) + +for f in rel_files: + test.must_exist(f) + test.must_match(f, example_rel_file, mode='r') + +example_abs_file = get_db(test.workdir, True, ['-g']) for f in abs_files: test.must_exist(f) diff --git a/test/CompilationDatabase/fixture/SConstruct b/test/CompilationDatabase/fixture/SConstruct index ea23bc3053..971e82156f 100644 --- a/test/CompilationDatabase/fixture/SConstruct +++ b/test/CompilationDatabase/fixture/SConstruct @@ -7,6 +7,7 @@ env = Environment( LINKFLAGS=[], CC='$PYTHON mygcc.py cc', CXX='$PYTHON mygcc.py c++', + CCFLAGS=ARGUMENTS.get('CCFLAGS', ''), tools=['gcc','g++','gnulink'], ) env.Tool('compilation_db') diff --git a/test/fixture/mygcc.py b/test/fixture/mygcc.py index ceb10e5661..32a9fca32a 100644 --- a/test/fixture/mygcc.py +++ b/test/fixture/mygcc.py @@ -3,7 +3,7 @@ compiler = sys.argv[1] clen = len(compiler) + 1 -opts, args = getopt.getopt(sys.argv[2:], 'co:xf:K:') +opts, args = getopt.getopt(sys.argv[2:], 'co:xf:gK:') for opt, arg in opts: if opt == '-o': out = arg From 5f07d1209963d3a5fbd14aa54c0253f2fc07ffb2 Mon Sep 17 00:00:00 2001 From: Emanuele Giaquinta Date: Sun, 5 Jul 2020 09:28:31 +0300 Subject: [PATCH 2/2] compilation_db: regenerate the db only if the content has changed --- SCons/Tool/compilation_db.py | 96 ++++++++----------------------- test/CompilationDatabase/basic.py | 9 +++ 2 files changed, 32 insertions(+), 73 deletions(-) diff --git a/SCons/Tool/compilation_db.py b/SCons/Tool/compilation_db.py index b610574a24..ebae23169b 100644 --- a/SCons/Tool/compilation_db.py +++ b/SCons/Tool/compilation_db.py @@ -45,19 +45,6 @@ __COMPILATION_DB_ENTRIES = [] -# We make no effort to avoid rebuilding the entries. Someday, perhaps we could and even -# integrate with the cache, but there doesn't seem to be much call for it. -class __CompilationDbNode(SCons.Node.Python.Value): - def __init__(self, value): - SCons.Node.Python.Value.__init__(self, value) - self.Decider(changed_since_last_build_node) - - -def changed_since_last_build_node(child, target, prev_ni, node): - """ Dummy decider to force always building""" - return True - - def make_emit_compilation_DB_entry(comstr): """ Effectively this creates a lambda function to capture: @@ -79,74 +66,40 @@ def emit_compilation_db_entry(target, source, env): :return: target(s), source(s) """ - dbtarget = __CompilationDbNode(source) - - entry = env.__COMPILATIONDB_Entry( - target=dbtarget, - source=[], - __COMPILATIONDB_UOUTPUT=target, - __COMPILATIONDB_USOURCE=source, - __COMPILATIONDB_UACTION=user_action, - __COMPILATIONDB_ENV=env, - ) - - # TODO: Technically, these next two lines should not be required: it should be fine to - # cache the entries. However, they don't seem to update properly. Since they are quick - # to re-generate disable caching and sidestep this problem. - env.AlwaysBuild(entry) - env.NoCache(entry) - - __COMPILATION_DB_ENTRIES.append(dbtarget) + entry = { + "directory": env.Dir("#").abspath, + "command": user_action.strfunction( + target=target, source=source, env=env + ), + "file": { + "abspath": source[0].srcnode().abspath, + "path": source[0].srcnode().path + }, + "output": { + "abspath": target[0].abspath, + "path": target[0].path + } + } + + __COMPILATION_DB_ENTRIES.append(entry) return target, source return emit_compilation_db_entry -def compilation_db_entry_action(target, source, env, **kw): - """ - Create a dictionary with evaluated command line, target, source - and store that info as an attribute on the target - (Which has been stored in __COMPILATION_DB_ENTRIES array - :param target: target node(s) - :param source: source node(s) - :param env: Environment for use building this node - :param kw: - :return: None - """ - - command = env["__COMPILATIONDB_UACTION"].strfunction( - target=env["__COMPILATIONDB_UOUTPUT"], - source=env["__COMPILATIONDB_USOURCE"], - env=env["__COMPILATIONDB_ENV"], - ) - - entry = { - "directory": env.Dir("#").abspath, - "command": command, - "file": env["__COMPILATIONDB_USOURCE"][0], - "output": env['__COMPILATIONDB_UOUTPUT'][0] - } - - target[0].write(entry) - - def write_compilation_db(target, source, env): entries = [] use_abspath = env['COMPILATIONDB_USE_ABSPATH'] in [True, 1, 'True', 'true'] - for s in __COMPILATION_DB_ENTRIES: - entry = s.read() - source_file = entry['file'] - output_file = entry['output'] - + for entry in __COMPILATION_DB_ENTRIES: if use_abspath: - source_file = source_file.srcnode().abspath - output_file = output_file.abspath + source_file = entry['file']['abspath'] + output_file = entry['output']['abspath'] else: - source_file = source_file.srcnode().path - output_file = output_file.path + source_file = entry['file']['path'] + output_file = entry['output']['path'] path_entry = {'directory': entry['directory'], 'command': entry['command'], @@ -162,7 +115,8 @@ def write_compilation_db(target, source, env): def scan_compilation_db(node, env, path): - return __COMPILATION_DB_ENTRIES + value = json.dumps(__COMPILATION_DB_ENTRIES, sort_keys=True) + return [SCons.Node.Python.Value(value)] def compilation_db_emitter(target, source, env): @@ -230,10 +184,6 @@ def generate(env, **kwargs): [emitter, make_emit_compilation_DB_entry(command), ] ) - env["BUILDERS"]["__COMPILATIONDB_Entry"] = SCons.Builder.Builder( - action=SCons.Action.Action(compilation_db_entry_action, None), - ) - env["BUILDERS"]["CompilationDatabase"] = SCons.Builder.Builder( action=SCons.Action.Action(write_compilation_db, "$COMPILATIONDB_COMSTR"), target_scanner=SCons.Scanner.Scanner( diff --git a/test/CompilationDatabase/basic.py b/test/CompilationDatabase/basic.py index d86300a5c2..03a388d6bc 100644 --- a/test/CompilationDatabase/basic.py +++ b/test/CompilationDatabase/basic.py @@ -78,12 +78,15 @@ def get_db(workdir, use_abspath, ccflags): ] +stat_info = {} + example_rel_file = get_db(test.workdir, False, []) for f in rel_files: # print("Checking:%s" % f) test.must_exist(f) test.must_match(f, example_rel_file, mode='r') + stat_info[f] = os.stat(f) example_abs_file = get_db(test.workdir, True, []) @@ -91,6 +94,12 @@ def get_db(workdir, use_abspath, ccflags): for f in abs_files: test.must_exist(f) test.must_match(f, example_abs_file, mode='r') + stat_info[f] = os.stat(f) + + +test.run() +for f in stat_info.keys(): + test.fail_test(stat_info[f] != os.stat(f)) test.run(arguments='CCFLAGS=-g')