From 60c2438c4aec9b4bf1c64878534e83152ab7a88e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 24 Jul 2018 10:46:16 +0200 Subject: Switch to new GCC module interface (-fmodule-mapper) --- build2/cc/compile-rule.cxx | 234 ++++++++++++++++++--------------------------- build2/cc/types.hxx | 6 +- build2/cxx/init.cxx | 25 ++--- 3 files changed, 112 insertions(+), 153 deletions(-) diff --git a/build2/cc/compile-rule.cxx b/build2/cc/compile-rule.cxx index a41203b..9e90017 100644 --- a/build2/cc/compile-rule.cxx +++ b/build2/cc/compile-rule.cxx @@ -3217,24 +3217,36 @@ namespace build2 if (dd.expect (cs.string ()) != nullptr) updating = true; -#if 0 // Save the module map for compilers that use it. // - if (md.mods.start != 0) + switch (cid) { - switch (cid) + case compiler_id::gcc: { - case compiler_id::gcc: - case compiler_id::clang: - case compiler_id::clang_apple: + // We don't need to redo this if the above hash hasn't changed and + // the database is still valid. + // + if (dd.writing () || !dd.skip ()) { - // We don't need to redo this if the above hash hasn't changed and - // the database is valid. - // - if (dd.writing () || !dd.skip ()) + auto write = [&dd] (const string& name, const path& file) { - const auto& pts (t.prerequisite_targets); + dd.write ("@ ", false); + dd.write (name, false); + dd.write (' ', false); + dd.write (file); + }; + // The output mapping is provided in the same way as input. + // + if (mi.iface) + write (mi.name, t.path ()); + + if (md.mods.start != 0) + { + // Note that we map both direct and indirect imports to override + // any module paths that might be stored in the BMIs. + // + const auto& pts (t.prerequisite_targets[a]); for (size_t i (md.mods.start); i != pts.size (); ++i) { if (const target* m = pts[i]) @@ -3242,20 +3254,17 @@ namespace build2 // Save a variable lookup by getting the module name from // the import list (see search_modules()). // - dd.write ('@', false); - dd.write (mi.imports[i - md.mods.start].name, false); - dd.write ('=', false); - dd.write (m->as ().path ()); + write (mi.imports[i - md.mods.start].name, + m->as ().path ()); } } } - break; } - default: break; } + default: + break; } -#endif // Set the cc.module_name variable if this is an interface unit. Note // that it may seem like a good idea to set it on the bmi{} group to @@ -4046,27 +4055,39 @@ namespace build2 const match_data& md) const { const module_positions& ms (md.mods); - assert (ms.start != 0); - dir_path stdifc; // See the VC case below. - auto& pts (t.prerequisite_targets[a]); - -#if 0 switch (cid) { case compiler_id::gcc: { // Use the module map stored in depdb. // - string s (relative (md.dd).string ()); - s.insert (0, "-fmodule-file-map=@="); - stor.push_back (move (s)); + // Note that it is also used to specify the output BMI file. + // + if (ms.start != 0 || md.type == translation_type::module_iface) + { + string s (relative (md.dd).string ()); + s.insert (0, "-fmodule-mapper="); + s += "?@"; // Cookie (aka line prefix). + stor.push_back (move (s)); + } + break; } case compiler_id::clang: case compiler_id::clang_apple: { + if (ms.start == 0) + return; + + // Clang embeds module file references so we only need to specify + // our direct imports. + // + // If/when we get the ability to specify the mapping in a file, we + // will pass the whole list. + // +#if 0 // In Clang the module implementation's unit .pcm is special and // must be "loaded". // @@ -4083,10 +4104,47 @@ namespace build2 string s (relative (md.dd).string ()); s.insert (0, "-fmodule-file-map=@="); stor.push_back (move (s)); +#else + auto& pts (t.prerequisite_targets[a]); + for (size_t i (ms.start), + n (ms.copied != 0 ? ms.copied : pts.size ()); + i != n; + ++i) + { + const target* pt (pts[i]); + + if (pt == nullptr) + continue; + + // Here we use whatever bmi type has been added. And we know all + // of these are bmi's. + // + const file& f (pt->as ()); + string s (relative (f.path ()).string ()); + + // In Clang the module implementation's unit .pcm is special and + // must be "loaded". + // + if (md.type == translation_type::module_impl && i == ms.start) + s.insert (0, "-fmodule-file="); + else + { + s.insert (0, 1, '='); + s.insert (0, cast (f.vars[c_module_name])); + s.insert (0, "-fmodule-file="); + } + + stor.push_back (move (s)); + } +#endif break; } case compiler_id::msvc: { + if (ms.start == 0) + return; + + auto& pts (t.prerequisite_targets[a]); for (size_t i (ms.start), n (pts.size ()); i != n; ++i) @@ -4133,102 +4191,6 @@ namespace build2 case compiler_id::icc: assert (false); } -#else - size_t n (pts.size ()); - - // Clang embeds module file references so we only need to specify - // our direct imports. - // - // If/when we get the ability to specify the mapping in a file, we - // should probably pass the whole list. - // - switch (cid) - { - case compiler_id::gcc: break; // All of them. - case compiler_id::clang_apple: - case compiler_id::clang: n = ms.copied != 0 ? ms.copied : n; break; - case compiler_id::msvc: break; // All of them. - case compiler_id::icc: assert (false); - } - - for (size_t i (ms.start); i != n; ++i) - { - const target* pt (pts[i]); - - if (pt == nullptr) - continue; - - // Here we use whatever bmi type has been added. And we know all of - // these are bmi's. - // - const file& f (pt->as ()); - string s (relative (f.path ()).string ()); - - switch (cid) - { - case compiler_id::gcc: - { - s.insert (0, 1, '='); - s.insert (0, cast (f.vars[c_module_name])); - s.insert (0, "-fmodule-file="); - break; - } - case compiler_id::clang: - case compiler_id::clang_apple: - { - // In Clang the module implementation's unit .pcm is special and - // must be "loaded". - // - if (md.type == translation_type::module_impl && i == ms.start) - s.insert (0, "-fmodule-file="); - else - { - s.insert (0, 1, '='); - s.insert (0, cast (f.vars[c_module_name])); - s.insert (0, "-fmodule-file="); - } - break; - } - case compiler_id::msvc: - { - // In VC std.* modules can only come from a single directory - // specified with the IFCPATH environment variable or the - // /module:stdIfcDir option. - // - if (std_module (cast (f.vars[c_module_name]))) - { - dir_path d (f.path ().directory ()); - - if (stdifc.empty ()) - { - // Go one directory up since /module:stdIfcDir will look in - // either Release or Debug subdirectories. Keeping the result - // absolute feels right. - // - s = d.directory ().string (); - stor.push_back ("/module:stdIfcDir"); - stdifc = move (d); - } - else - { - if (d != stdifc) // Absolute and normalized. - fail << "multiple std.* modules in different directories"; - - continue; // Skip. - } - } - else - stor.push_back ("/module:reference"); - - break; - } - case compiler_id::icc: - assert (false); - } - - stor.push_back (move (s)); - } -#endif // Shallow-copy storage to args. Why not do it as we go along pushing // into storage? Because of potential reallocations. @@ -4298,14 +4260,8 @@ namespace build2 // If we are building a module, then the target is bmi*{} and its ad hoc // member is obj*{}. // - path relo, relm; - if (mod) - { - relm = relative (tp); - relo = relative (t.member->is_a ()->path ()); - } - else - relo = relative (tp); + path relm; + path relo (relative (mod ? t.member->is_a ()->path () : tp)); // Build the command line. // @@ -4382,8 +4338,7 @@ namespace build2 if (!find_option_prefixes ({"/MD", "/MT"}, args)) args.push_back ("/MD"); - if (md.mods.start != 0) - append_modules (env, args, mods, a, t, md); + append_modules (env, args, mods, a, t, md); // The presence of /Zi or /ZI causes the compiler to write debug info // to the .pdb file. By default it is a shared file called vcNN.pdb @@ -4426,6 +4381,8 @@ namespace build2 if (mod) { + relm = relative (tp); + args.push_back ("/module:interface"); args.push_back ("/module:output"); args.push_back (relm.string ().c_str ()); @@ -4447,8 +4404,7 @@ namespace build2 args.push_back ("-fPIC"); } - if (md.mods.start != 0) - append_modules (env, args, mods, a, t, md); + append_modules (env, args, mods, a, t, md); // Note: the order of the following options is relied upon below. // @@ -4460,19 +4416,19 @@ namespace build2 { case compiler_id::gcc: { + // Output module file is specified in the mapping file, the + // same as input. + // args.push_back ("-o"); args.push_back (relo.string ().c_str ()); - - out = "-fmodule-output="; - out += relm.string (); - args.push_back (out.c_str ()); - args.push_back ("-c"); break; } case compiler_id::clang: case compiler_id::clang_apple: { + relm = relative (tp); + args.push_back ("-o"); args.push_back (relm.string ().c_str ()); args.push_back ("--precompile"); diff --git a/build2/cc/types.hxx b/build2/cc/types.hxx index 6dc7632..d50c067 100644 --- a/build2/cc/types.hxx +++ b/build2/cc/types.hxx @@ -27,9 +27,9 @@ namespace build2 struct module_info { - string name; // Not empty if a module unit. - bool iface; // True if a module interface unit. - module_imports imports; // Imported modules. + string name; // Not empty if a module unit. + bool iface = false; // True if a module interface unit. + module_imports imports; // Imported modules. }; enum class translation_type {plain, module_iface, module_impl}; diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx index 6621757..1f5eba3 100644 --- a/build2/cxx/init.cxx +++ b/build2/cxx/init.cxx @@ -182,14 +182,17 @@ namespace build2 } case compiler_id::gcc: { - // Enable starting with GCC 8.0.0 (currently the c++-modules + // Enable starting with GCC 9.0.0 (currently the c++-modules // branch). // if (l && // Barely usable at the moment. - mj >= 8 && + mj >= 9 && ci.version.build.find ("c++-modules") != string::npos) { - r.push_back ("-fmodules"); + // Currently defines __cpp_modules=201804 which is said to + // correspond to p0713 ('module;' leading marker). + // + r.push_back ("-fmodules-ts"); modules = true; } break; @@ -395,14 +398,14 @@ namespace build2 v["cc.module_name"], v["cc.reprocess"], - // Ability to indicate that source is already (partially) - // preprocessed. Valid values are 'none' (not preprocessed), - // 'includes' (no #include directives in source), 'modules' (as above - // plus no module declaration depends on preprocessor, e.g., #ifdef, - // etc), and 'all' (the source is fully preprocessed). Note that for - // 'all' the source can still contain comments and line - // continuations. Note also that for some compilers (e.g., VC) there - // is no way to signal that the source is already preprocessed. + // Ability to signal that source is already (partially) preprocessed. + // Valid values are 'none' (not preprocessed), 'includes' (no #include + // directives in source), 'modules' (as above plus no module + // declaration depends on preprocessor, e.g., #ifdef, etc), and 'all' + // (the source is fully preprocessed). Note that for 'all' the source + // can still contain comments and line continuations. Note also that + // for some compilers (e.g., VC) there is no way to signal that the + // source is already preprocessed. // v.insert ("cxx.preprocessed"), -- cgit v1.1