From 6caa434e947a95a6afa4be5dcac87266dd7508ac Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 30 Mar 2018 16:38:23 +0200 Subject: Add support for libraries in new command --- bdep/new.cxx | 327 +++++++++++++++++++++++++++++++++++++++++++++++++------ bdep/utility.hxx | 2 + 2 files changed, 294 insertions(+), 35 deletions(-) diff --git a/bdep/new.cxx b/bdep/new.cxx index 3e3b3b8..b2648d4 100644 --- a/bdep/new.cxx +++ b/bdep/new.cxx @@ -69,9 +69,9 @@ namespace bdep } } - // Validate type options. + // Validate vcs options. // - const vcs& s (o.vcs ()); + const vcs& v (o.vcs ()); // Validate argument. // @@ -81,8 +81,19 @@ namespace bdep //@@ TODO: verify valid package name (put the helper in libbpkg). - if (o.type () == type::lib && n.compare (0, 3, "lib") != 0) - fail << "library name does not start with 'lib'"; + // Full name vs the name stem (e.g, 'hello' in 'libhello'). + // + // We use the full name for filesystem directories and preprocessor macros + // while the stem for modules, namespaces, etc. + // + string s; + if (o.type () == type::lib) + { + if (n.compare (0, 3, "lib") != 0) + fail << "library name does not start with 'lib'"; + + s.assign (n, 3); + } dir_path prj (n); prj.complete (); @@ -98,7 +109,7 @@ namespace bdep // Initialize the version control system. Do it before writing anything // ourselves in case it fails. // - switch (s) + switch (v) { case vcs::git: run ("git", "init", "-q", prj); break; case vcs::none: break; @@ -115,7 +126,7 @@ namespace bdep os << ": 1" << endl << "name: " << n << endl << "version: 0.1.0-a.0.z" << endl - << "summary: new " << t << " project" << endl + << "summary: " << s << " " << t << " project" << endl << "license: proprietary" << endl << "url: https://example.org/" << n << endl << "email: you@example.org" << endl @@ -168,9 +179,7 @@ namespace bdep << "using c" << endl << endl << "h{*}: extension = h" << endl - << "c{*}: extension = c" << endl - << endl - << "c.poptions =+ \"-I$out_root\" \"-I$src_root\"" << endl; + << "c{*}: extension = c" << endl; break; } case lang::cxx: @@ -184,9 +193,7 @@ namespace bdep << "hxx{*}: extension = h" << s << endl << "ixx{*}: extension = i" << s << endl << "txx{*}: extension = t" << s << endl - << "cxx{*}: extension = c" << s << endl - << endl - << "cxx.poptions =+ \"-I$out_root\" \"-I$src_root\"" << endl; + << "cxx{*}: extension = c" << s << endl; break; } } @@ -194,7 +201,7 @@ namespace bdep // build/.gitignore // - if (s == vcs::git) + if (v == vcs::git) { os.open (f = bd / ".gitignore"); os << "config.build" << endl @@ -210,7 +217,7 @@ namespace bdep // .gitignore // - if (s == vcs::git) + if (v == vcs::git) { // Use POSIX directory separators here. // @@ -244,10 +251,7 @@ namespace bdep dir_path sd (dir_path (prj) /= n); if (t != type::bare) - { mk (sd); - os.open (f = sd / "buildfile"); - } switch (t) { @@ -257,11 +261,6 @@ namespace bdep { case lang::c: { - // buildfile - // - os << "exe{" << n << "}: {h c}{*}" << endl; - os.close (); - // .c // os.open (f = sd / n + ".c"); @@ -273,20 +272,16 @@ namespace bdep << " return 0;" << endl << "}" << endl; os.close (); + break; } case lang::cxx: { - // buildfile - // - os << "exe{" << n << "}: {hxx ixx txx cxx}{*}" << endl; - os.close (); - - const char* s (l.cxx_opt.cpp () ? "pp" : "xx"); + string x (l.cxx_opt.cpp () ? "pp" : "xx"); // .c(xx|pp) // - os.open (f = sd / n + ".c" + s); + os.open (f = sd / n + ".c" + x); os << "#include " << endl << endl << "int main ()" << endl @@ -294,38 +289,300 @@ namespace bdep << " std::cout << \"Hello, World!\" << std::endl;" << endl << "}" << endl; os.close (); + + break; + } + } + + // buildfile + // + os.open (f = sd / "buildfile"); + os << "libs =" << endl + << "#import libs += libhello%lib{hello}" << endl + << endl; + + const char* x (nullptr); // Language module. + switch (l) + { + case lang::c: + { + os << "exe{" << n << "}: {h c}{*} $libs" << endl; + + x = "c"; + break; + } + case lang::cxx: + { + os << "exe{" << n << "}: {hxx ixx txx cxx}{*} $libs" << endl; + + x = "cxx"; break; } } + + os << endl + << x << ".poptions =+ \"-I$out_root\" \"-I$src_root\"" << endl; + os.close (); + break; } case type::lib: { + string m; // Macro prefix. + transform ( + n.begin (), n.end (), back_inserter (m), + [] (char c) + { + return (c == '-' || c == '+' || c == '.') ? '_' : ucase (c); + }); + + string exp; // Export header name. + string ver; // Version header name. + switch (l) { case lang::c: { - // buildfile + exp = "export.h"; + ver = "version.h"; + + // .h // - os << "lib{" << n << "}: {h c}{*}" << endl; + os.open (f = sd / s + ".h"); + os << "#pragma once" << endl + << endl + << "#include <" << n << "/" << exp << ">" << endl + << endl + << m << "_SYMEXPORT void" << endl + << "say_hello (const char* name);" << endl; os.close (); - //@@ TODO + // .c + // + os.open (f = sd / s + ".c"); + os << "#include <" << n << "/" << s << ".h" << ">" << endl + << endl + << "#include " << endl + << endl + << "#include <" << n << "/" << ver << ">" << endl + << endl + << "void" << endl + << "say_hello (const char* n)" << endl + << "{" << endl + << " printf (\"Hello, %s from " << n << " %s!\\n\", n, " << m << "_VERSION_ID);" << endl + << "}" << endl; + os.close (); break; } case lang::cxx: { - // buildfile + string x (l.cxx_opt.cpp () ? "pp" : "xx"); + + exp = "export.h" + x; + ver = "version.h" + x; + + // .h(xx|pp) + // + os.open (f = sd / s + ".h" + x); + os << "#pragma once" << endl + << endl + << "#include " << endl + << endl + << "#include <" << n << "/" << exp << ">" << endl + << endl + << "namespace " << s << endl + << "{" << endl + << " " << m << "_SYMEXPORT void" << endl + << " say_hello (const std::string& name);" << endl + << "}" << endl; + os.close (); + + // .c(xx|pp) // - os << "lib{" << n << "}: {hxx ixx txx cxx}{*}" << endl; + os.open (f = sd / s + ".c" + x); + os << "#include <" << n << "/" << s << ".h" << x << ">" << endl + << endl + << "#include " << endl + << endl + << "#include <" << n << "/" << ver << ">" << endl + << endl + << "using namespace std;" << endl + << endl + << "namespace " << s << endl + << "{" << endl + << " void" << endl + << " say_hello (const string& n)" << endl + << " {" << endl + << " cout << \"Hello, \" << n << \" from \"" << endl + << " << \"" << n << " \" << " << m << "_VERSION_ID << '!' << endl;" << endl + << " }" << endl + << "}" << endl; os.close (); - //@@ TODO + break; + } + } + + // export.h[??] + // + os.open (f = sd / exp); + os << "#pragma once" << endl + << endl; + if (l == lang::cxx) + { + os << "// Normally we don't export class templates (but do complete specializations)," << endl + << "// inline functions, and classes with only inline member functions. Exporting" << endl + << "// classes that inherit from non-exported/imported bases (e.g., std::string)" << endl + << "// will end up badly. The only known workarounds are to not inherit or to not" << endl + << "// export. Also, MinGW GCC doesn't like seeing non-exported function being" << endl + << "// used before their inline definition. The workaround is to reorder code. In" << endl + << "// the end it's all trial and error." << endl + << endl; + } + os << "#if defined(" << m << "_STATIC) // Using static." << endl + << "# define " << m << "_SYMEXPORT" << endl + << "#elif defined(" << m << "_STATIC_BUILD) // Building static." << endl + << "# define " << m << "_SYMEXPORT" << endl + << "#elif defined(" << m << "_SHARED) // Using shared." << endl + << "# ifdef _WIN32" << endl + << "# define " << m << "_SYMEXPORT __declspec(dllimport)" << endl + << "# else" << endl + << "# define " << m << "_SYMEXPORT" << endl + << "# endif" << endl + << "#elif defined(" << m << "_SHARED_BUILD) // Building shared." << endl + << "# ifdef _WIN32" << endl + << "# define " << m << "_SYMEXPORT __declspec(dllexport)" << endl + << "# else" << endl + << "# define " << m << "_SYMEXPORT" << endl + << "# endif" << endl + << "#else" << endl + << "// If none of the above macros are defined, then we assume we are being used" << endl + << "// by some third-party build system that cannot/doesn't signal the library" << endl + << "// type. Note that this fallback works for both static and shared but in case" << endl + << "// of shared will be sub-optimal compared to having dllimport." << endl + << "//" << endl + << "# define " << m << "_SYMEXPORT // Using static or shared." << endl + << "#endif" << endl; + os.close (); + + // version.h[??].in + // + os.open (f = sd / ver + ".in"); + os << "#pragma once" << endl + << endl + << "// The numeric version format is AAABBBCCCDDDE where:"<< endl + << "//" << endl + << "// AAA - major version number" << endl + << "// BBB - minor version number" << endl + << "// CCC - bugfix version number" << endl + << "// DDD - alpha / beta (DDD + 500) version number" << endl + << "// E - final (0) / snapshot (1)" << endl + << "//" << endl + << "// When DDDE is not 0, 1 is subtracted from AAABBBCCC. For example:" << endl + << "//" << endl + << "// Version AAABBBCCCDDDE" << endl + << "//" << endl + << "// 0.1.0 0000010000000" << endl + << "// 0.1.2 0000010010000" << endl + << "// 1.2.3 0010020030000" << endl + << "// 2.2.0-a.1 0020019990010" << endl + << "// 3.0.0-b.2 0029999995020" << endl + << "// 2.2.0-a.1.z 0020019990011" << endl + << "//" << endl + << "#define " << m << "_VERSION $" << n << ".version.project_number$ULL" << endl + << "#define " << m << "_VERSION_STR \"$" << n << ".version.project$\"" << endl + << "#define " << m << "_VERSION_ID \"$" << n << ".version.project_id$\"" << endl + << endl + << "#define " << m << "_VERSION_MAJOR $" << n << ".version.major$" << endl + << "#define " << m << "_VERSION_MINOR $" << n << ".version.minor$" << endl + << "#define " << m << "_VERSION_PATCH $" << n << ".version.patch$" << endl + << endl + << "#define " << m << "_PRE_RELEASE $" << n << ".version.pre_release$" << endl + << endl + << "#define " << m << "_SNAPSHOT_SN $" << n << ".version.snapshot_sn$ULL" << endl + << "#define " << m << "_SNAPSHOT_ID \"$" << n << ".version.snapshot_id$\"" << endl; + os.close (); + + // buildfile + // + os.open (f = sd / "buildfile"); + os << "int_libs = # Interface dependencies." << endl + << "imp_libs = # Implementation dependencies." << endl + << "#import imp_libs += libhello%lib{hello}" << endl + << endl; + + const char* x (nullptr); // Language module. + const char* h (nullptr); // Header target type. + const char* hs (nullptr); // All header target types. + switch (l) + { + case lang::c: + { + os << "lib{" << s << "}: {h c}{* -version} h{version}" << endl; + + x = "c"; + h = "h"; + hs = "h"; + break; + } + case lang::cxx: + { + os << "lib{" << s << "}: {hxx ixx txx cxx}{* -version} hxx{version} $imp_libs $int_libs" << endl; + x = "cxx"; + h = "hxx"; + hs = "hxx ixx txx"; break; } } + + os << endl + << "# Include the generated version header into the distribution (so that we don't" << endl + << "# pick up an installed one) and don't remove it when cleaning in src (so that" << endl + << "# clean results in a state identical to distributed)." << endl + << "#" << endl + << h << "{version}: in{version} $src_root/file{manifest}" << endl + << h << "{version}: dist = true" << endl + << h << "{version}: clean = ($src_root != $out_root)" << endl + << endl + << x << ".poptions =+ \"-I$out_root\" \"-I$src_root\"" << endl + << "lib{" << s << "}: " << x << ".export.poptions = \"-I$out_root\" \"-I$src_root\"" << endl + << endl + << "liba{" << s << "}: " << x << ".export.poptions += -D" << m << "_STATIC" << endl + << "libs{" << s << "}: " << x << ".export.poptions += -D" << m << "_SHARED" << endl + << endl + << "obja{*}: " << x << ".poptions += -D" << m << "_STATIC_BUILD" << endl + << "objs{*}: " << x << ".poptions += -D" << m << "_SHARED_BUILD" << endl + << endl + << "lib{" << s << "}: " << x << ".export.libs = $int_libs" << endl + << endl + << "# For pre-releases use the complete version to make sure they cannot be used" << endl + << "# in place of another pre-release or the final version." << endl + << "#" << endl + << "if $version.pre_release" << endl + << " lib{" << s << "}: bin.lib.version = @\"-$version.project_id\"" << endl + << "else" << endl + << " lib{" << s << "}: bin.lib.version = @\"-$version.major.$version.minor\"" << endl + << endl + << "# Install into the " << n << "/ subdirectory of, say, /usr/include/" << endl + << "# recreating subdirectories." << endl + << "#" << endl + << "{" << hs << "}{*}: install = include/$project/" << endl + << "{" << hs << "}{*}: install.subdirs = true" << endl; + os.close (); + + // build/export.build + // + os.open (f = bd / "export.build"); + os << "$out_root/:" << endl + << "{" << endl + << " include " << n << "/" << endl + << "}" << endl + << endl + << "export $out_root/" << n << "/lib{" << s << "}" << endl; + os.close (); + break; } case type::bare: diff --git a/bdep/utility.hxx b/bdep/utility.hxx index 865268a..4831bab 100644 --- a/bdep/utility.hxx +++ b/bdep/utility.hxx @@ -37,6 +37,8 @@ namespace bdep // // + using butl::ucase; + using butl::lcase; using butl::casecmp; using butl::reverse_iterate; -- cgit v1.1