summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-09-28 18:03:28 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-09-28 18:03:28 +0200
commit297bc80eab5128e1cde7cd597368109a033b6cc0 (patch)
tree251806183b14a8b643b2bfe1c934c73022c90628
parenta00f98381c708fbba986844762368be3d0e20a6f (diff)
Update idea: C++ modules support
Add module test for Clang and VC
-rw-r--r--build2/cxx-modules/modtest/README129
-rw-r--r--build2/cxx-modules/modtest/bar43
-rw-r--r--build2/cxx-modules/modtest/bar.cxx35
-rw-r--r--build2/cxx-modules/modtest/build/bootstrap.build10
-rw-r--r--build2/cxx-modules/modtest/build/root.build16
-rw-r--r--build2/cxx-modules/modtest/buildfile63
-rw-r--r--build2/cxx-modules/modtest/driver.cxx51
-rw-r--r--build2/cxx-modules/modtest/foo54
-rw-r--r--build2/cxx-modules/modtest/foo.cxx41
-rw-r--r--build2/cxx-modules/modtest/module.modulemap9
10 files changed, 451 insertions, 0 deletions
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=<module-id> 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 <string>
+
+// 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 <string>
+
+#include <bar>
+
+//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 <ifc-file> 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 <string>
+#include <iostream>
+
+// 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 <foo>
+# include <bar>
+
+// 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 <foo>
+# 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 <string>
+
+// 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 <string>
+
+#include <foo>
+
+//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"
+}