aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-03-30 16:38:23 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-03-30 16:38:23 +0200
commit6caa434e947a95a6afa4be5dcac87266dd7508ac (patch)
treebcd610de0f9f8a67b1577a0a78331d17039111ab
parent4d0d7321806d72252521e227a27afd6d8e06a887 (diff)
Add support for libraries in new command
-rw-r--r--bdep/new.cxx327
-rw-r--r--bdep/utility.hxx2
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 ();
-
// <name>.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");
// <name>.c(xx|pp)
//
- os.open (f = sd / n + ".c" + s);
+ os.open (f = sd / n + ".c" + x);
os << "#include <iostream>" << 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";
+
+ // <stem>.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
+ // <stem>.c
+ //
+ os.open (f = sd / s + ".c");
+ os << "#include <" << n << "/" << s << ".h" << ">" << endl
+ << endl
+ << "#include <stdio.h>" << 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;
+
+ // <stem>.h(xx|pp)
+ //
+ os.open (f = sd / s + ".h" + x);
+ os << "#pragma once" << endl
+ << endl
+ << "#include <string>" << 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 ();
+
+ // <stem>.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 <iostream>" << 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
// <libbutl/utility.mxx>
//
+ using butl::ucase;
+ using butl::lcase;
using butl::casecmp;
using butl::reverse_iterate;