aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/cc/compile-rule.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2020-11-05 15:52:13 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2020-11-05 15:52:13 +0200
commit8671b4dc516994b7f070a341c004aab28e525431 (patch)
treeae44903ad4960ed5ace4bcb26401be5dd592ff4f /libbuild2/cc/compile-rule.cxx
parentfb4272e816ad949811f9f8b709771c5aeb646d1f (diff)
Initial Emscripten support
- Target: wasm32-emscripten (wasm32-unknown-emscripten). - Compiler id: clang-emscripten (type clang, variant emscripten, class gcc). - Ability to build executables (.js plus .wasm) and static libraries (.a). Set executable bit on the .js file (so it can be executed with a suitable binfmt interpreter). - Default config.bin.lib for wasm32-emscripten is static instead of both. - Full C++ exception support is enable unless disabled explicitly by the user with -s DISABLE_EXCEPTION_CATCHING=1|2. - The bin module registers the wasm{} target type for wasm32-emscripten.
Diffstat (limited to 'libbuild2/cc/compile-rule.cxx')
-rw-r--r--libbuild2/cc/compile-rule.cxx523
1 files changed, 286 insertions, 237 deletions
diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx
index 8274a04..832ed9b 100644
--- a/libbuild2/cc/compile-rule.cxx
+++ b/libbuild2/cc/compile-rule.cxx
@@ -3124,6 +3124,18 @@ namespace build2
}
}
+ if (ctype == compiler_type::clang && cvariant == "emscripten")
+ {
+ if (x_lang == lang::cxx)
+ {
+ if (!find_option_prefix ("DISABLE_EXCEPTION_CATCHING=", args))
+ {
+ args.push_back ("-s");
+ args.push_back ("DISABLE_EXCEPTION_CATCHING=0");
+ }
+ }
+ }
+
append_options (args, cmode,
cmode.size () - (modules && clang ? 1 : 0));
append_sys_inc_options (args); // Extra system header dirs (last).
@@ -4355,6 +4367,18 @@ namespace build2
}
}
+ if (ctype == compiler_type::clang && cvariant == "emscripten")
+ {
+ if (x_lang == lang::cxx)
+ {
+ if (!find_option_prefix ("DISABLE_EXCEPTION_CATCHING=", args))
+ {
+ args.push_back ("-s");
+ args.push_back ("DISABLE_EXCEPTION_CATCHING=0");
+ }
+ }
+ }
+
append_options (args, cmode,
cmode.size () - (modules && clang ? 1 : 0));
append_sys_inc_options (args);
@@ -5921,299 +5945,324 @@ namespace build2
size_t out_i (0); // Index of the -o option.
size_t lang_n (0); // Number of lang options.
- if (cclass == compiler_class::msvc)
+ switch (cclass)
{
- // The /F*: option variants with separate names only became available
- // in VS2013/12.0. Why do we bother? Because the command line suddenly
- // becomes readable.
- //
- // Also, clang-cl does not yet support them, at least not in 8 or 9.
- //
- bool fc (cmaj >= 18 && cvariant != "clang");
+ case compiler_class::msvc:
+ {
+ // The /F*: option variants with separate names only became
+ // available in VS2013/12.0. Why do we bother? Because the command
+ // line suddenly becomes readable.
+ //
+ // Also, clang-cl does not yet support them, at least not in 8 or 9.
+ //
+ bool fc (cmaj >= 18 && cvariant != "clang");
- args.push_back ("/nologo");
+ args.push_back ("/nologo");
- append_options (args, cmode);
+ append_options (args, cmode);
- if (md.pp != preprocessed::all)
- append_sys_inc_options (args); // Extra system header dirs (last).
+ if (md.pp != preprocessed::all)
+ append_sys_inc_options (args); // Extra system header dirs (last).
+
+ // While we want to keep the low-level build as "pure" as possible,
+ // the two misguided defaults, C++ exceptions and runtime, just have
+ // to be fixed. Otherwise the default build is pretty much unusable.
+ // But we also make sure that the user can easily disable our
+ // defaults: if we see any relevant options explicitly specified, we
+ // take our hands off.
+ //
+ // For C looks like no /EH* (exceptions supported but no C++ objects
+ // destroyed) is a reasonable default.
+ //
+ if (x_lang == lang::cxx && !find_option_prefix ("/EH", args))
+ args.push_back ("/EHsc");
+
+ // The runtime is a bit more interesting. At first it may seem like
+ // a good idea to be a bit clever and use the static runtime if we
+ // are building obja{}. And for obje{} we could decide which runtime
+ // to use based on the library link order: if it is static-only,
+ // then we could assume the static runtime. But it is indeed too
+ // clever: when building liba{} we have no idea who is going to use
+ // it. It could be an exe{} that links both static and shared
+ // libraries (and is therefore built with the shared runtime). And
+ // to safely use the static runtime, everything must be built with
+ // /MT and there should be no DLLs in the picture. So we are going
+ // to play it safe and always default to the shared runtime.
+ //
+ // In a similar vein, it would seem reasonable to use the debug
+ // runtime if we are compiling with debug. But, again, there will be
+ // fireworks if we have some projects built with debug and some
+ // without and then we try to link them together (which is not an
+ // unreasonable thing to do). So by default we will always use the
+ // release runtime.
+ //
+ if (!find_option_prefixes ({"/MD", "/MT"}, args))
+ args.push_back ("/MD");
- // While we want to keep the low-level build as "pure" as possible,
- // the two misguided defaults, C++ exceptions and runtime, just have
- // to be fixed. Otherwise the default build is pretty much unusable.
- // But we also make sure that the user can easily disable our
- // defaults: if we see any relevant options explicitly specified, we
- // take our hands off.
- //
- // For C looks like no /EH* (exceptions supported but no C++ objects
- // destroyed) is a reasonable default.
- //
- if (x_lang == lang::cxx && !find_option_prefix ("/EH", args))
- args.push_back ("/EHsc");
-
- // The runtime is a bit more interesting. At first it may seem like a
- // good idea to be a bit clever and use the static runtime if we are
- // building obja{}. And for obje{} we could decide which runtime to
- // use based on the library link order: if it is static-only, then we
- // could assume the static runtime. But it is indeed too clever: when
- // building liba{} we have no idea who is going to use it. It could be
- // an exe{} that links both static and shared libraries (and is
- // therefore built with the shared runtime). And to safely use the
- // static runtime, everything must be built with /MT and there should
- // be no DLLs in the picture. So we are going to play it safe and
- // always default to the shared runtime.
- //
- // In a similar vein, it would seem reasonable to use the debug runtime
- // if we are compiling with debug. But, again, there will be fireworks
- // if we have some projects built with debug and some without and then
- // we try to link them together (which is not an unreasonable thing to
- // do). So by default we will always use the release runtime.
- //
- if (!find_option_prefixes ({"/MD", "/MT"}, args))
- args.push_back ("/MD");
+ msvc_sanitize_cl (args);
- msvc_sanitize_cl (args);
+ append_header_options (env, args, header_args, a, t, md, md.dd);
+ append_module_options (env, args, module_args, a, t, md, md.dd);
- append_header_options (env, args, header_args, a, t, md, md.dd);
- append_module_options (env, args, module_args, a, t, md, md.dd);
+ // 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 (where NN is the VC version) created (wait for it) in
+ // the current working directory (and not the directory of the .obj
+ // file). Also, because it is shared, there is a special Windows
+ // service that serializes access. We, of course, want none of that
+ // so we will create a .pdb per object file.
+ //
+ // Note that this also changes the name of the .idb file (used for
+ // minimal rebuild and incremental compilation): cl.exe take the /Fd
+ // value and replaces the .pdb extension with .idb.
+ //
+ // Note also that what we are doing here appears to be incompatible
+ // with PCH (/Y* options) and /Gm (minimal rebuild).
+ //
+ if (find_options ({"/Zi", "/ZI"}, args))
+ {
+ if (fc)
+ args.push_back ("/Fd:");
+ else
+ out1 = "/Fd";
+
+ out1 += relo.string ();
+ out1 += ".pdb";
+
+ args.push_back (out1.c_str ());
+ }
- // 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
- // (where NN is the VC version) created (wait for it) in the current
- // working directory (and not the directory of the .obj file). Also,
- // because it is shared, there is a special Windows service that
- // serializes access. We, of course, want none of that so we will
- // create a .pdb per object file.
- //
- // Note that this also changes the name of the .idb file (used for
- // minimal rebuild and incremental compilation): cl.exe take the /Fd
- // value and replaces the .pdb extension with .idb.
- //
- // Note also that what we are doing here appears to be incompatible
- // with PCH (/Y* options) and /Gm (minimal rebuild).
- //
- if (find_options ({"/Zi", "/ZI"}, args))
- {
if (fc)
- args.push_back ("/Fd:");
+ {
+ args.push_back ("/Fo:");
+ args.push_back (relo.string ().c_str ());
+ }
else
- out1 = "/Fd";
+ {
+ out = "/Fo" + relo.string ();
+ args.push_back (out.c_str ());
+ }
- out1 += relo.string ();
- out1 += ".pdb";
+ // @@ MODHDR MSVC
+ //
+ if (ut == unit_type::module_iface)
+ {
+ relm = relative (tp);
- args.push_back (out1.c_str ());
- }
+ args.push_back ("/module:interface");
+ args.push_back ("/module:output");
+ args.push_back (relm.string ().c_str ());
+ }
- if (fc)
- {
- args.push_back ("/Fo:");
- args.push_back (relo.string ().c_str ());
- }
- else
- {
- out = "/Fo" + relo.string ();
- args.push_back (out.c_str ());
- }
+ // Note: no way to indicate that the source if already preprocessed.
- // @@ MODHDR MSVC
- //
- if (ut == unit_type::module_iface)
- {
- relm = relative (tp);
+ args.push_back ("/c"); // Compile only.
+ append_lang_options (args, md); // Compile as.
+ args.push_back (sp->string ().c_str ()); // Note: relied on being last.
- args.push_back ("/module:interface");
- args.push_back ("/module:output");
- args.push_back (relm.string ().c_str ());
+ break;
}
-
- // Note: no way to indicate that the source if already preprocessed.
-
- args.push_back ("/c"); // Compile only.
- append_lang_options (args, md); // Compile as.
- args.push_back (sp->string ().c_str ()); // Note: relied on being last.
- }
- else
- {
- if (ot == otype::s)
+ case compiler_class::gcc:
{
- // On Darwin, Win32 -fPIC is the default.
- //
- if (tclass == "linux" || tclass == "bsd")
- args.push_back ("-fPIC");
- }
+ if (ot == otype::s)
+ {
+ // On Darwin, Win32 -fPIC is the default.
+ //
+ if (tclass == "linux" || tclass == "bsd")
+ args.push_back ("-fPIC");
+ }
- if (tsys == "win32-msvc")
- {
- switch (ctype)
+ if (tsys == "win32-msvc")
{
- case compiler_type::clang:
+ switch (ctype)
{
- // Default to the /EHsc exceptions support for C++, similar to
- // the the MSVC case above.
- //
- // Note that both vanilla clang++ and clang-cl drivers add
- // -fexceptions and -fcxx-exceptions by default. However,
- // clang-cl also adds -fexternc-nounwind, which implements the
- // 'c' part in /EHsc. Note that adding this option is not a mere
- // optimization, as we have discovered through some painful
- // experience; see Clang bug #45021.
- //
- // Let's also omit this option if -f[no]-exceptions is specified
- // explicitly.
- //
- if (x_lang == lang::cxx)
+ case compiler_type::clang:
{
- if (!find_options ({"-fexceptions", "-fno-exceptions"}, args))
+ // Default to the /EHsc exceptions support for C++, similar to
+ // the the MSVC case above.
+ //
+ // Note that both vanilla clang++ and clang-cl drivers add
+ // -fexceptions and -fcxx-exceptions by default. However,
+ // clang-cl also adds -fexternc-nounwind, which implements the
+ // 'c' part in /EHsc. Note that adding this option is not a
+ // mere optimization, as we have discovered through some
+ // painful experience; see Clang bug #45021.
+ //
+ // Let's also omit this option if -f[no]-exceptions is
+ // specified explicitly.
+ //
+ if (x_lang == lang::cxx)
{
- args.push_back ("-Xclang");
- args.push_back ("-fexternc-nounwind");
+ if (!find_options ({"-fexceptions", "-fno-exceptions"}, args))
+ {
+ args.push_back ("-Xclang");
+ args.push_back ("-fexternc-nounwind");
+ }
}
- }
- // Default to the multi-threaded DLL runtime (/MD), similar to
- // the MSVC case above.
- //
- // Clang's MSVC.cpp will not link the default runtime if either
- // -nostdlib or -nostartfiles is specified. Let's do the same.
- //
- initializer_list<const char*> os {"-nostdlib", "-nostartfiles"};
- if (!find_options (os, cmode) && !find_options (os, args))
- {
- args.push_back ("-D_MT");
- args.push_back ("-D_DLL");
-
- // All these -Xclang --dependent-lib=... add quite a bit of
- // noise to the command line. The alternative is to use the
- // /DEFAULTLIB option during linking. The drawback of that
- // approach is that now we can theoretically build the object
- // file for one runtime but try to link it with something
- // else.
- //
- // For example, an installed static library was built for a
- // non-debug runtime while a project that links it uses
- // debug. With the --dependent-lib approach we will try to
- // link multiple runtimes while with /DEFAULTLIB we may end up
- // with unresolved symbols (but things might also work out
- // fine, unless the runtimes have incompatible ABIs).
+ // Default to the multi-threaded DLL runtime (/MD), similar to
+ // the MSVC case above.
//
- // Let's start with /DEFAULTLIB and see how it goes (see the
- // link rule).
+ // Clang's MSVC.cpp will not link the default runtime if
+ // either -nostdlib or -nostartfiles is specified. Let's do
+ // the same.
//
+ initializer_list<const char*> os {"-nostdlib", "-nostartfiles"};
+ if (!find_options (os, cmode) && !find_options (os, args))
+ {
+ args.push_back ("-D_MT");
+ args.push_back ("-D_DLL");
+
+ // All these -Xclang --dependent-lib=... add quite a bit of
+ // noise to the command line. The alternative is to use the
+ // /DEFAULTLIB option during linking. The drawback of that
+ // approach is that now we can theoretically build the
+ // object file for one runtime but try to link it with
+ // something else.
+ //
+ // For example, an installed static library was built for a
+ // non-debug runtime while a project that links it uses
+ // debug. With the --dependent-lib approach we will try to
+ // link multiple runtimes while with /DEFAULTLIB we may end
+ // up with unresolved symbols (but things might also work
+ // out fine, unless the runtimes have incompatible ABIs).
+ //
+ // Let's start with /DEFAULTLIB and see how it goes (see the
+ // link rule).
+ //
#if 0
- args.push_back ("-Xclang");
- args.push_back ("--dependent-lib=msvcrt");
+ args.push_back ("-Xclang");
+ args.push_back ("--dependent-lib=msvcrt");
- // This provides POSIX compatibility (map open() to _open(),
- // etc).
- //
- args.push_back ("-Xclang");
- args.push_back ("--dependent-lib=oldnames");
+ // This provides POSIX compatibility (map open() to _open(),
+ // etc).
+ //
+ args.push_back ("-Xclang");
+ args.push_back ("--dependent-lib=oldnames");
#endif
+ }
+
+ break;
}
+ case compiler_type::gcc:
+ case compiler_type::msvc:
+ case compiler_type::icc:
+ assert (false);
+ }
+ }
- break;
+ // For now Emscripten defaults to partial C++ exceptions support
+ // (you can throw but not catch). We enable full support unless it
+ // was explicitly disabled by the user.
+ //
+ if (ctype == compiler_type::clang && cvariant == "emscripten")
+ {
+ if (x_lang == lang::cxx)
+ {
+ if (!find_option_prefix ("DISABLE_EXCEPTION_CATCHING=", args))
+ {
+ args.push_back ("-s");
+ args.push_back ("DISABLE_EXCEPTION_CATCHING=0");
+ }
}
- case compiler_type::gcc:
- case compiler_type::msvc:
- case compiler_type::icc:
- assert (false);
}
- }
- append_options (args, cmode);
+ append_options (args, cmode);
- if (md.pp != preprocessed::all)
- append_sys_inc_options (args); // Extra system header dirs (last).
+ if (md.pp != preprocessed::all)
+ append_sys_inc_options (args); // Extra system header dirs (last).
- append_header_options (env, args, header_args, a, t, md, md.dd);
- append_module_options (env, args, module_args, a, t, md, md.dd);
+ append_header_options (env, args, header_args, a, t, md, md.dd);
+ append_module_options (env, args, module_args, a, t, md, md.dd);
- // Note: the order of the following options is relied upon below.
- //
- out_i = args.size (); // Index of the -o option.
+ // Note: the order of the following options is relied upon below.
+ //
+ out_i = args.size (); // Index of the -o option.
- if (ut == unit_type::module_iface || ut == unit_type::module_header)
- {
- switch (ctype)
+ if (ut == unit_type::module_iface || ut == unit_type::module_header)
{
- case compiler_type::gcc:
+ switch (ctype)
{
- // Output module file is specified in the mapping file, the
- // same as input.
- //
- if (ut != unit_type::module_header) // No object file.
+ case compiler_type::gcc:
{
- args.push_back ("-o");
- args.push_back (relo.string ().c_str ());
- args.push_back ("-c");
+ // Output module file is specified in the mapping file, the
+ // same as input.
+ //
+ if (ut != unit_type::module_header) // No object file.
+ {
+ args.push_back ("-o");
+ args.push_back (relo.string ().c_str ());
+ args.push_back ("-c");
+ }
+ break;
}
- break;
- }
- case compiler_type::clang:
- {
- relm = relative (tp);
+ case compiler_type::clang:
+ {
+ relm = relative (tp);
- args.push_back ("-o");
- args.push_back (relm.string ().c_str ());
- args.push_back ("--precompile");
+ args.push_back ("-o");
+ args.push_back (relm.string ().c_str ());
+ args.push_back ("--precompile");
- // Without this option Clang's .pcm will reference source files.
- // In our case this file may be transient (.ii). Plus, it won't
- // play nice with distributed compilation.
- //
- args.push_back ("-Xclang");
- args.push_back ("-fmodules-embed-all-files");
+ // Without this option Clang's .pcm will reference source
+ // files. In our case this file may be transient (.ii). Plus,
+ // it won't play nice with distributed compilation.
+ //
+ args.push_back ("-Xclang");
+ args.push_back ("-fmodules-embed-all-files");
- break;
+ break;
+ }
+ case compiler_type::msvc:
+ case compiler_type::icc:
+ assert (false);
}
- case compiler_type::msvc:
- case compiler_type::icc:
- assert (false);
}
- }
- else
- {
- args.push_back ("-o");
- args.push_back (relo.string ().c_str ());
- args.push_back ("-c");
- }
+ else
+ {
+ args.push_back ("-o");
+ args.push_back (relo.string ().c_str ());
+ args.push_back ("-c");
+ }
- lang_n = append_lang_options (args, md);
+ lang_n = append_lang_options (args, md);
- if (md.pp == preprocessed::all)
- {
- // Note that the mode we select must still handle comments and line
- // continuations. So some more compiler-specific voodoo.
- //
- switch (ctype)
+ if (md.pp == preprocessed::all)
{
- case compiler_type::gcc:
+ // Note that the mode we select must still handle comments and
+ // line continuations. So some more compiler-specific voodoo.
+ //
+ switch (ctype)
{
- // -fdirectives-only is available since GCC 4.3.0.
- //
- if (cmaj > 4 || (cmaj == 4 && cmin >= 3))
+ case compiler_type::gcc:
{
- args.push_back ("-fpreprocessed");
- args.push_back ("-fdirectives-only");
+ // -fdirectives-only is available since GCC 4.3.0.
+ //
+ if (cmaj > 4 || (cmaj == 4 && cmin >= 3))
+ {
+ args.push_back ("-fpreprocessed");
+ args.push_back ("-fdirectives-only");
+ }
+ break;
}
- break;
- }
- case compiler_type::clang:
- {
- // Clang handles comments and line continuations in the
- // preprocessed source (it does not have -fpreprocessed).
- //
- break;
+ case compiler_type::clang:
+ {
+ // Clang handles comments and line continuations in the
+ // preprocessed source (it does not have -fpreprocessed).
+ //
+ break;
+ }
+ case compiler_type::icc:
+ break; // Compile as normal source for now.
+ case compiler_type::msvc:
+ assert (false);
}
- case compiler_type::icc:
- break; // Compile as normal source for now.
- case compiler_type::msvc:
- assert (false);
}
- }
- args.push_back (sp->string ().c_str ());
+ args.push_back (sp->string ().c_str ());
+
+ break;
+ }
}
args.push_back (nullptr);