C++ module compilation works for a simple project.

pull/8013/head
Jussi Pakkanen 4 years ago
parent 92e94b1e92
commit f390d22787
  1. 26
      mesonbuild/backend/ninjabackend.py
  2. 87
      mesonbuild/scripts/depscan.py

@ -134,6 +134,11 @@ Please report this error with a test case to the Meson bug tracker.'''.format(te
raise MesonException(errmsg)
return quote_re.sub(r'$\g<0>', text)
class TargetDependencyScannerInfo:
def __init__(self, private_dir, source2object):
self.private_dir = private_dir
self.source2object = source2object
@unique
class Quoting(Enum):
both = 0
@ -683,6 +688,10 @@ int dummy;
return False
def generate_target(self, target):
try:
os.makedirs(self.get_target_private_dir_abs(target))
except FileExistsError:
pass
if isinstance(target, build.CustomTarget):
self.generate_custom_target(target)
if isinstance(target, build.RunTarget):
@ -843,7 +852,7 @@ int dummy;
o, s = self.generate_single_compile(target, src, False, [], header_deps)
obj_list.append(o)
compiled_sources.append(s)
source2object[src] = o
source2object[s] = o
obj_list += self.flatten_object_list(target)
if is_unity:
@ -851,7 +860,7 @@ int dummy;
o, s = self.generate_single_compile(target, src, True, unity_deps + header_deps)
obj_list.append(o)
compiled_sources.append(s)
source2object[src] = o
source2object[s] = o
linker, stdlib_args = self.determine_linker_and_stdlib_args(target)
if isinstance(target, build.StaticLibrary) and target.prelink:
final_obj_list = self.generate_prelink(target, obj_list)
@ -866,10 +875,15 @@ int dummy;
if 'cpp' not in target.compilers:
return
depscan_file = self.get_dep_scan_file_for(target)
pickle_file = os.path.join(self.get_target_private_dir(target), target.name + '.dat').replace('\\', '/')
pickle_base = target.name + '.dat'
pickle_file = os.path.join(self.get_target_private_dir(target), pickle_base).replace('\\', '/')
pickle_abs = os.path.join(self.get_target_private_dir_abs(target), pickle_base).replace('\\', '/')
rule_name = 'cppscan'
elem = NinjaBuildElement(self.all_outputs, depscan_file, rule_name, compiled_sources)
elem.add_item('picklefile', pickle_file)
scaninfo = TargetDependencyScannerInfo(self.get_target_private_dir(target), source2object)
with open(pickle_abs, 'wb') as p:
pickle.dump(scaninfo, p)
self.add_build(elem)
def process_target_dependencies(self, target):
@ -1998,7 +2012,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
# Scanning command is the same for native and cross compilation.
continue
command = cmd = self.environment.get_build_command() + \
['--internal', 'scan']
['--internal', 'depscan']
args = ['$picklefile', '$out', '$in']
description = 'Module scanner for {}.'.format(langname)
rule = NinjaRule(rulename, command, args, description)
@ -2490,7 +2504,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
self.add_dependency_scanner_entries_to_element(target, compiler, element)
self.add_build(element)
return (rel_obj, rel_src)
assert(isinstance(rel_obj, str))
assert(isinstance(rel_src, str))
return (rel_obj, rel_src.replace('\\', '/'))
def add_dependency_scanner_entries_to_element(self, target, compiler, element):
if compiler.get_language() != 'cpp':

@ -0,0 +1,87 @@
# Copyright 2020 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import pathlib
import pickle
import re
import typing as T
import_re = re.compile('\w*import ([a-zA-Z0-9]+);')
export_re = re.compile('\w*export module ([a-zA-Z0-9]+);')
class DependencyScanner:
def __init__(self, pickle_file, outfile, sources):
with open(pickle_file, 'rb') as pf:
self.target_data = pickle.load(pf)
self.outfile = outfile
self.sources = sources
self.provided_by = {}
self.exports = {}
self.needs = {}
self.sources_with_exports = []
def scan_file(self, fname):
for line in pathlib.Path(fname).read_text().split('\n'):
import_match = import_re.match(line)
export_match = export_re.match(line)
if import_match:
needed = import_match.group(1)
if fname in self.needs:
self.needs[fname].append(needed)
else:
self.needs[fname] = [needed]
if export_match:
exported_module = export_match.group(1)
if exported_module in self.provided_by:
raise RuntimeError('Multiple files provide module {}.'.format(exported_module))
self.sources_with_exports.append(fname)
self.provided_by[exported_module] = fname
self.exports[fname] = exported_module
def objname_for(self, src):
return self.target_data.source2object[src]
def ifcname_for(self, src):
return '{}.ifc'.format(self.exports[src])
def scan(self):
for s in self.sources:
self.scan_file(s)
with open(self.outfile, 'w') as ofile:
ofile.write('ninja_dyndep_version = 1\n')
for src in self.sources:
objfilename = self.objname_for(src)
if src in self.sources_with_exports:
ifc_entry = '| ' + self.ifcname_for(src)
else:
ifc_entry = ''
if src in self.needs:
# FIXME, handle all sources, not just the first one
modname = self.needs[src][0]
provider_src = self.provided_by[modname]
provider_ifc = self.ifcname_for(provider_src)
mod_dep = '| ' + provider_ifc
else:
mod_dep = ''
ofile.write('build {} {}: dyndep {}\n'.format(objfilename,
ifc_entry,
mod_dep))
return 0
def run(args: T.List[str]) -> int:
pickle_file = args[0]
outfile = args[1]
sources = args[2:]
scanner = DependencyScanner(pickle_file, outfile, sources)
return scanner.scan()
Loading…
Cancel
Save