From 297bc80eab5128e1cde7cd597368109a033b6cc0 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 28 Sep 2016 18:03:28 +0200 Subject: Update idea: C++ modules support Add module test for Clang and VC --- build2/cxx-modules/modtest/README | 129 +++++++++++++++++++++++ build2/cxx-modules/modtest/bar | 43 ++++++++ build2/cxx-modules/modtest/bar.cxx | 35 ++++++ build2/cxx-modules/modtest/build/bootstrap.build | 10 ++ build2/cxx-modules/modtest/build/root.build | 16 +++ build2/cxx-modules/modtest/buildfile | 63 +++++++++++ build2/cxx-modules/modtest/driver.cxx | 51 +++++++++ build2/cxx-modules/modtest/foo | 54 ++++++++++ build2/cxx-modules/modtest/foo.cxx | 41 +++++++ build2/cxx-modules/modtest/module.modulemap | 9 ++ 10 files changed, 451 insertions(+) create mode 100644 build2/cxx-modules/modtest/README create mode 100644 build2/cxx-modules/modtest/bar create mode 100644 build2/cxx-modules/modtest/bar.cxx create mode 100644 build2/cxx-modules/modtest/build/bootstrap.build create mode 100644 build2/cxx-modules/modtest/build/root.build create mode 100644 build2/cxx-modules/modtest/buildfile create mode 100644 build2/cxx-modules/modtest/driver.cxx create mode 100644 build2/cxx-modules/modtest/foo create mode 100644 build2/cxx-modules/modtest/foo.cxx create mode 100644 build2/cxx-modules/modtest/module.modulemap diff --git a/build2/cxx-modules/modtest/README b/build2/cxx-modules/modtest/README new file mode 100644 index 0000000..2cb4743 --- /dev/null +++ b/build2/cxx-modules/modtest/README @@ -0,0 +1,129 @@ +Use use_modules=false command line variable override to compile without +modules: + +$ b config.cxx=clang++ use_modules=true test +$ b config.cxx=cl-14u2 use_modules=true test + +Other useful options: + +config.cxx.coptions+=-stdlib=libc++ + +For now use config.bin.lib=static if compile with VC. + +CLang 3.7.0, 3.9.0 +================== + +Notes: + +* What is a header file for clang? Looks like it considers it a module. But + what if definitions are contained in a separate (corresponding source) + file. It sounds like such a file also imports the module it implements. + Sounds wierd. + + Probably need to add -fmodule-name= when compile module source + file(s). Should make some difference (which externally is invisible). + +* When clang sees #include it compiles the included file (with a separate + compiler instance) and place the result into the cache. + +* clang++ -module-file-info foo-8KLLM232DU5X.pcm prints lot of information + about foo module. + +* module.modulemap file(s) appears in the output of the "show dependencies" + command like that: + + clang++ -I`pwd` -fmodules -std=c++1y -M -MG -MQ ^ driver.cxx + +Issues: + +* First run of b when configured to use CLang fails with: + fatal error: error in backend: IO failure on output stream. + + Some bug in clang++ 3.7.0 which seems to relate to the creation/update of + translated modules cache while using -M (dependency generation) option. + Consequitive runs (even those which require module recompilation) work well. + + clang 3.8.0, 3.9.0 work fine. + +VC 14 U2,3 +========== + +Notes: + +* Module interface description get persisted into the separate ifc-file when + the module source file is compiled: foo.cxx -> foo.obj, foo.ifc. To compile + a source file which imports a module the compiler expects module's ifc-file + to be available. + + That's currently requres to maintain a proper prerequisite's order in + buildfile. When build an executable consuming module located in a library + need to ensure the library source files are compiled before executable's + source files to get lib's ifc-files ready: + + $ b config.cxx=cl-14u2 use_modules=true 'lib{bar}' + $ b config.cxx=cl-14u2 use_modules=true + +* There is a ifc.exe tool (comes with VC) which can be used to embed interface + files into static lib or convert to object file. + +* No signs of the tools/compiler switch to extract a module dependencies from a + source file. Some people complains/asks about it: + + http://nibblestew.blogspot.ru/2015/10/some-comments-on-c-modules-talk.html + http://stackoverflow.com/questions/35230327/how-to-use-vc-modules-in-cmake + +Issues: + +* Using std::string in the struct foo results in VC 14 U2 with linker's + inability to resolve string symbols while linking foo.exe.obj. VC 14 U3 + compiler in this case fails with internal error while compiling driver.cxx. + + CLang 3.9.0 works ok in this case. + +* How to only produce .ifc file without the object file? /module:export? + + Looks like compiling the header (with /TP) is the best options so far: it + produces .obj but a small one. + + cl /c /EHsc /experimental:module /module:interface /TP foo + +References +========== + +A Module System for C++ (Revision 4): + + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0142r0.pdf + +-- + +CLang modules: + + http://clang.llvm.org/docs/Modules.html + +CLang module tests (examples): + + https://github.com/llvm-mirror/clang/tree/master/test/Modules + +Some early presentation (Feb 2012, Doug Gregor) + + https://isocpp.org/blog/2012/11/modules-update-on-work-in-progress-doug-gregor + +-- + +Modules Support in Visual C++ 2015 Update 1: + + https://blogs.msdn.microsoft.com/vcblog/2015/12/03/c-modules-in-vs-2015-update-1/ + +Compiler improvements in VS 2015 Update 2 (in regards of C++ Modules there are +seems to be just bug fixes): + + https://blogs.msdn.microsoft.com/vcblog/2016/02/11/compiler-improvements-in-vs-2015-update-2/ + +MS C++ Modules talk on CppCon 2015: + + https://github.com/isocpp/CppCoreGuidelines/blob/master/talks/Large-Scale-C%2B%2B-With-Modules.pdf + https://www.youtube.com/watch?v=RwdQA0pGWa4 + +VC 14 Update 3 + + https://www.visualstudio.com/en-us/news/releasenotes/vs2015-update3-vs diff --git a/build2/cxx-modules/modtest/bar b/build2/cxx-modules/modtest/bar new file mode 100644 index 0000000..bc14c7c --- /dev/null +++ b/build2/cxx-modules/modtest/bar @@ -0,0 +1,43 @@ +// file : bar -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +// #include + +// Define bar module interface. +// +// For VC must be included only into module implementation (bar.cxx). The +// module should be imported into consumer file with import declaration. +// +// For CLang must be #included into both module implementation and consumer +// files. +// + +#if defined(MODTEST_USE_MODULES) && defined(_MSC_VER) +module bar; +export +{ +#endif + +int +bar_value (int v); + +struct bar +{ + explicit bar (int v); + + int + value (); + +// std::string +// message (const char* s) const; + +private: + int v_; +}; + +// Close module export declaration. +// +#if defined(MODTEST_USE_MODULES) && defined(_MSC_VER) +} +#endif diff --git a/build2/cxx-modules/modtest/bar.cxx b/build2/cxx-modules/modtest/bar.cxx new file mode 100644 index 0000000..457f29e --- /dev/null +++ b/build2/cxx-modules/modtest/bar.cxx @@ -0,0 +1,35 @@ +// file : bar.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +//#include + +#include + +//using namespace std; + +int +bar_value (int v) +{ + return v * 100; +} + +bar:: +bar (int v) + : v_ (v) +{ +} + +int bar:: +value () +{ + return v_ * 1000; +} + +/* +string bar:: +message (const char* s) const +{ + return string ("bar: ") + s; +} +*/ diff --git a/build2/cxx-modules/modtest/build/bootstrap.build b/build2/cxx-modules/modtest/build/bootstrap.build new file mode 100644 index 0000000..0c03d81 --- /dev/null +++ b/build2/cxx-modules/modtest/build/bootstrap.build @@ -0,0 +1,10 @@ +# file : build/bootstrap.build +# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +project = modtest + +using build@0.4.0 + +using config +using test diff --git a/build2/cxx-modules/modtest/build/root.build b/build2/cxx-modules/modtest/build/root.build new file mode 100644 index 0000000..7c4d659 --- /dev/null +++ b/build2/cxx-modules/modtest/build/root.build @@ -0,0 +1,16 @@ +# file : build/root.build +# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +cxx.std = 14 + +using cxx + +hxx{*}: extension = +ixx{*}: extension = ixx +txx{*}: extension = txx +cxx{*}: extension = cxx + +# All exe{} are, well, tests. +# +exe{*}: test = true diff --git a/build2/cxx-modules/modtest/buildfile b/build2/cxx-modules/modtest/buildfile new file mode 100644 index 0000000..4aa779b --- /dev/null +++ b/build2/cxx-modules/modtest/buildfile @@ -0,0 +1,63 @@ +# file : buildfile +# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +./: exe{driver} + +lib{bar}: {hxx cxx}{bar} + +# The order of prerequisites is important. When compile with VC using modules +# the module interface file (foo.ifc) should be produced before module consumer +# (driver.cxx) is compiled. The same reasoning is applied for bar.ifc. +# +if ($cxx.id == "msvc") + exe{driver}: lib{bar} {hxx cxx}{foo} cxx{driver} +else + exe{driver}: {hxx cxx}{foo} cxx{driver} lib{bar} + +cxx.poptions =+ -I$src_root + +if ($use_modules == true) +{ + cxx.poptions += -DMODTEST_USE_MODULES + + if ($cxx.id == "clang") + { + # -Wno-ambiguous-macro - required to suppress "ambiguous expansion of macro + # MODTEST_MACRO" warning (do not mix up with macro + # redefinition warning). The warning seems to follow + # from a module macro leakage effect. + # + cxx.coptions += -fmodules -Wno-ambiguous-macro \ + -fmodules-cache-path=$out_root/modcache + + # Frankly not 100% sure this is required. + # + obj{foo}: cxx.coptions += -fmodule-name=foo + obj{bar}: cxx.coptions += -fmodule-name=bar + } + if ($cxx.id == "clang-apple") + { + # While compiler (8.0.0) recognizes -fmodules* options they just get + # ignored as no import module semantics is assigned to #include directive. + # + # @@ Can there be something wrong with module.modulemap file? + # + cxx.coptions += -fmodules -fmodules-cache-path=$out_root/modcache + + obj{foo}: cxx.coptions += -fmodule-name=foo + obj{bar}: cxx.coptions += -fmodule-name=bar + } + elif ($cxx.id == "msvc") + { + # /module:interface - produce ifc-file if there is interface definition + # in a file. + # /module:search - directory to search for ifc-files. In its absense + # need to use /module:reference option when + # compile consumer of the module represented with the + # corresponding ifc-file. + # + cxx.coptions += /experimental:module /module:interface \ + /module:search $out_base + } +} diff --git a/build2/cxx-modules/modtest/driver.cxx b/build2/cxx-modules/modtest/driver.cxx new file mode 100644 index 0000000..e782e85 --- /dev/null +++ b/build2/cxx-modules/modtest/driver.cxx @@ -0,0 +1,51 @@ +// file : driver.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +//#include +#include + +// For macro isolation test. +// +#define MODTEST_MACRO 2 + +// VC introduces module-related declarations (import, export, module). CLang +// doesn't do that implementing some transitional model interpreting headers as +// modules according to a special "module.modulemap" file and performing import +// in place of #include. +// +#if defined(MODTEST_USE_MODULES) && defined(_MSC_VER) + +import foo; +import bar; + +#else + +# include +# include + +// If we are using modules and #include directive is effectivelly converted to +// the import declaration there should be no "redefinition of foo" error. Let's +// check that. +// +# ifdef MODTEST_USE_MODULES +# include +# endif + +#endif + +using namespace std; + +int +main () +{ + foo f (3); + cerr << "foo values: " << foo_value (5) << " " << f.value () << endl + << "foo macros: " + << (MODTEST_MACRO == f.macro () ? "leaks" : "isolated") << endl; + +// cerr << f.message ("Hi") << endl; + + bar b (7); + cerr << "bar values: " << bar_value (6) << " " << b.value () << endl; +} diff --git a/build2/cxx-modules/modtest/foo b/build2/cxx-modules/modtest/foo new file mode 100644 index 0000000..620816b --- /dev/null +++ b/build2/cxx-modules/modtest/foo @@ -0,0 +1,54 @@ +// file : foo -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +// #include + +// Define foo module interface. +// +// For VC must be included only into module implementation (foo.cxx). The +// module should be imported into consumer file (with the import declaration). +// +// For CLang must be #included into both module implementation and consumer +// files. +// + +#if defined(MODTEST_USE_MODULES) && defined(_MSC_VER) +module foo; +export +{ +#endif + +// Required by macro isolation test. +// +#ifdef MODTEST_MACRO +# undef MODTEST_MACRO +#endif + +#define MODTEST_MACRO 1 + +int +foo_value (int v); + +struct foo +{ + explicit foo (int v); + + int + value (); + + int + macro (); + +// std::string +// message (const char* s) const; + +private: + int v_; +}; + +// Close module export declaration. +// +#if defined(MODTEST_USE_MODULES) && defined(_MSC_VER) +} +#endif diff --git a/build2/cxx-modules/modtest/foo.cxx b/build2/cxx-modules/modtest/foo.cxx new file mode 100644 index 0000000..9a6dbd1 --- /dev/null +++ b/build2/cxx-modules/modtest/foo.cxx @@ -0,0 +1,41 @@ +// file : foo.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +//#include + +#include + +//using namespace std; + +int +foo_value (int v) +{ + return v * 10; +} + +foo:: +foo (int v) + : v_ (v) +{ +} + +int foo:: +value () +{ + return v_ * 100; +} + +int foo:: +macro () +{ + return MODTEST_MACRO; +} + +/* +string foo:: +message (const char* s) const +{ + return string ("foo: ") + s; +} +*/ diff --git a/build2/cxx-modules/modtest/module.modulemap b/build2/cxx-modules/modtest/module.modulemap new file mode 100644 index 0000000..3d12b55 --- /dev/null +++ b/build2/cxx-modules/modtest/module.modulemap @@ -0,0 +1,9 @@ +module foo { + requires cplusplus11 + header "foo" +} + +module bar { + requires cplusplus11 + header "bar" +} -- cgit v1.1