From 13cf46aa561cde57966fd3e924e5503ff51fadf2 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 3 May 2018 15:00:57 +0200 Subject: Add support for tests in new command --- bdep/new.cli | 20 +++- bdep/new.cxx | 364 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 349 insertions(+), 35 deletions(-) diff --git a/bdep/new.cli b/bdep/new.cli index f5f2a14..faad002 100644 --- a/bdep/new.cli +++ b/bdep/new.cli @@ -61,15 +61,26 @@ namespace bdep \li|\cb{exe} - A project that builds a sample executable.| + A project that builds a sample executable. Recognized executable + project options: + + \cb{no-tests} \- Don't add support for functional/integration testing. + | \li|\cb{lib} - A project that builds a sample library.| + A project that builds a sample library. Recognized library project + options: + + \cb{no-tests} \- Don't add support for functional/integration testing. + | \li|\cb{bare} - A project without any source code.|| + A project without any source code. Recognized bare project options: + + \cb{no-tests} \- Don't add support for functional/integration testing. + || The project language can be specified with the \c{\b{--lang}|\b{-l}} option. Valid values for this option and their semantics are described @@ -113,14 +124,17 @@ namespace bdep // class cmd_new_exe_options { + bool no-tests; }; class cmd_new_lib_options { + bool no-tests; }; class cmd_new_bare_options { + bool no-tests; }; // --lang options diff --git a/bdep/new.cxx b/bdep/new.cxx index 85acad4..5306b94 100644 --- a/bdep/new.cxx +++ b/bdep/new.cxx @@ -43,6 +43,11 @@ namespace bdep // const type& t (o.type ()); + bool tests (t == type::exe ? !t.exe_opt.no_tests () : + t == type::lib ? !t.lib_opt.no_tests () : + t == type::bare ? !t.bare_opt.no_tests () : false); + + // Validate language options. // const lang& l (o.lang ()); @@ -158,14 +163,17 @@ namespace bdep os << "project = " << n << endl << endl << "using version" << endl - << "using config" << endl - << "using test" << endl - << "using dist" << endl + << "using config" << endl; + if (tests) + os << "using test" << endl; + os << "using dist" << endl << "using install" << endl; os.close (); // build/root.build // + // Note: see also tests/build/root.build below. + // os.open (f = bd / "root.build"); switch (l) { @@ -213,10 +221,17 @@ namespace bdep // os.open (f = prj / "buildfile"); os << "./: {*/ -build/} file{manifest}" << endl; + if (tests && t == type::lib) // Have tests/ subproject. + os << endl + << "# Don't install tests." << endl + << "#" << endl + << "dir{tests/}: install = false" << endl; os.close (); // .gitignore // + // See also tests/.gitignore below. + // if (v == vcs::git) { // Use POSIX directory separators here. @@ -261,14 +276,20 @@ namespace bdep { case lang::c: { - // .c + // /.c // os.open (f = sd / n + ".c"); os << "#include " << endl << endl - << "int main ()" << endl + << "int main (int argc, char *argv[])" << endl << "{" << endl - << " printf (\"Hello, World!\\n\");" << endl + << " if (argc < 2)" << endl + << " {" << endl + << " fprintf (stderr, \"error: missing name\\n\");"<< endl + << " return 1;" << endl + << " }" << endl + << endl + << " printf (\"Hello, %s!\\n\", argv[1]);" << endl << " return 0;" << endl << "}" << endl; os.close (); @@ -279,14 +300,22 @@ namespace bdep { string x (l.cxx_opt.cpp () ? "pp" : "xx"); - // .c(xx|pp) + // /.c(xx|pp) // os.open (f = sd / n + ".c" + x); os << "#include " << endl << endl - << "int main ()" << endl + << "using namespace std;" << endl + << endl + << "int main (int argc, char* argv[])" << endl << "{" << endl - << " std::cout << \"Hello, World!\" << std::endl;" << endl + << " if (argc < 2)" << endl + << " {" << endl + << " cerr << \"error: missing name\" << endl;" << endl + << " return 1;" << endl + << " }" << endl + << endl + << " cout << \"Hello, \" << argv[1] << '!' << endl;" << endl << "}" << endl; os.close (); @@ -294,7 +323,7 @@ namespace bdep } } - // buildfile + // /buildfile // os.open (f = sd / "buildfile"); os << "libs =" << endl @@ -306,14 +335,16 @@ namespace bdep { case lang::c: { - os << "exe{" << n << "}: {h c}{*} $libs" << endl; + os << "exe{" << n << "}: {h c}{*} $libs" << + (tests ? " test{testscript}" : "") << endl; x = "c"; break; } case lang::cxx: { - os << "exe{" << n << "}: {hxx ixx txx cxx}{*} $libs" << endl; + os << "exe{" << n << "}: {hxx ixx txx cxx}{*} $libs" << + (tests ? " test{testscript}" : "") << endl; x = "cxx"; break; @@ -324,6 +355,37 @@ namespace bdep << x << ".poptions =+ \"-I$out_root\" \"-I$src_root\"" << endl; os.close (); + // /.gitignore + // + if (v == vcs::git) + { + os.open (f = sd / ".gitignore"); + os << n << endl; + if (tests) + os << endl + << "# Testscript output directory (can be symlink)." << endl + << "#" << endl + << "test-" << n << endl; + os.close (); + } + + // /testscript + // + if (!tests) + break; + + os.open (f = sd / "testscript"); + os << ": basics" << endl + << ":" << endl + << "$* 'World' >'Hello, World!'" << endl + << endl + << ": missing-name" << endl + << ":" << endl + << "$* 2>>EOE != 0" << endl + << "error: missing name" << endl + << "EOE" << endl; + os.close (); + break; } case type::lib: @@ -336,6 +398,7 @@ namespace bdep return (c == '-' || c == '+' || c == '.') ? '_' : ucase (c); }); + string hdr; // API header name. string exp; // Export header name. string ver; // Version header name. @@ -343,33 +406,43 @@ namespace bdep { case lang::c: { + hdr = s + ".h"; exp = "export.h"; ver = "version.h"; // .h // - os.open (f = sd / s + ".h"); + os.open (f = sd / hdr); os << "#pragma once" << endl << endl + << "#include " << endl + << endl << "#include <" << n << "/" << exp << ">" << endl << endl - << m << "_SYMEXPORT void" << endl - << "say_hello (const char* name);" << endl; + << "// Print a greeting for the specified name into the specified" << endl + << "// stream. On success, return the number of character printed." << endl + << "// On failure, set errno and return a negative value." << endl + << "//" << endl + << m << "_SYMEXPORT int" << endl + << "say_hello (FILE *, const char *name);" << endl; os.close (); // .c // os.open (f = sd / s + ".c"); - os << "#include <" << n << "/" << s << ".h" << ">" << endl - << endl - << "#include " << endl + os << "#include <" << n << "/" << hdr << ">" << endl << endl - << "#include <" << n << "/" << ver << ">" << endl + << "#include " << endl << endl - << "void" << endl - << "say_hello (const char* n)" << endl + << "int say_hello (FILE *f, const char* n)" << endl << "{" << endl - << " printf (\"Hello, %s from " << n << " %s!\\n\", n, " << m << "_VERSION_ID);" << endl + << " if (f == NULL || n == NULL || *n == '\\0')" << endl + << " {" << endl + << " errno = EINVAL;" << endl + << " return -1;" << endl + << " }" << endl + << endl + << " return fprintf (f, \"Hello, %s!\\n\", n);" << endl << "}" << endl; os.close (); @@ -379,43 +452,49 @@ namespace bdep { string x (l.cxx_opt.cpp () ? "pp" : "xx"); + hdr = s + ".h" + x; exp = "export.h" + x; ver = "version.h" + x; // .h(xx|pp) // - os.open (f = sd / s + ".h" + x); + os.open (f = sd / hdr); os << "#pragma once" << endl << endl + << "#include " << endl << "#include " << endl << endl << "#include <" << n << "/" << exp << ">" << endl << endl << "namespace " << s << endl << "{" << endl + << " // Print a greeting for the specified name into the specified" << endl + << " // stream. Throw std::invalid_argument if the name is empty." << endl + << " //" << endl << " " << m << "_SYMEXPORT void" << endl - << " say_hello (const std::string& name);" << endl + << " say_hello (std::ostream&, " << + "const std::string& name);" << endl << "}" << endl; os.close (); // .c(xx|pp) // os.open (f = sd / s + ".c" + x); - os << "#include <" << n << "/" << s << ".h" << x << ">" << endl + os << "#include <" << n << "/" << hdr << ">" << endl << endl - << "#include " << endl - << endl - << "#include <" << n << "/" << ver << ">" << endl + << "#include " << endl + << "#include " << endl << endl << "using namespace std;" << endl << endl << "namespace " << s << endl << "{" << endl - << " void" << endl - << " say_hello (const string& n)" << endl + << " void say_hello (ostream& o, const string& n)" << endl << " {" << endl - << " cout << \"Hello, \" << n << \" from \"" << endl - << " << \"" << n << " \" << " << m << "_VERSION_ID << '!' << endl;" << endl + << " if (n.empty ())" << endl + << " throw invalid_argument (\"empty name\");" << endl + << endl + << " o << \"Hello, \" << n << '!' << endl;" << endl << " }" << endl << "}" << endl; os.close (); @@ -572,6 +651,17 @@ namespace bdep << "{" << hs << "}{*}: install.subdirs = true" << endl; os.close (); + // /.gitignore + // + if (v == vcs::git) + { + os.open (f = sd / ".gitignore"); + os << "# Generated version header." << endl + << "#" << endl + << ver << endl; + os.close (); + } + // build/export.build // os.open (f = bd / "export.build"); @@ -583,6 +673,216 @@ namespace bdep << "export $out_root/" << n << "/lib{" << s << "}" << endl; os.close (); + // tests/ (tests subproject). + // + if (!tests) + break; + + dir_path td (dir_path (prj) /= "tests"); + mk (td); + + // tests/build/ + // + dir_path tbd (dir_path (td) /= "build"); + mk (tbd); + + // tests/build/bootstrap.build + // + os.open (f = tbd / "bootstrap.build"); + os << "project = # Unnamed tests subproject." << endl + << endl + << "using config" << endl + << "using test" << endl + << "using dist" << endl; + os.close (); + + // tests/build/root.build + // + os.open (f = tbd / "root.build"); + switch (l) + { + case lang::c: + { + // @@ TODO: 'latest' in c.std. + // + os //<< "c.std = latest" << endl + //<< endl + << "using c" << endl + << endl + << "h{*}: extension = h" << endl + << "c{*}: extension = c" << endl; + break; + } + case lang::cxx: + { + const char* s (l.cxx_opt.cpp () ? "pp" : "xx"); + + os << "cxx.std = latest" << endl + << endl + << "using cxx" << endl + << endl + << "hxx{*}: extension = h" << s << endl + << "ixx{*}: extension = i" << s << endl + << "txx{*}: extension = t" << s << endl + << "cxx{*}: extension = c" << s << endl; + break; + } + } + os << endl + << "# Every exe{} in this subproject is by default a test."<< endl + << "#" << endl + << "exe{*}: test = true" << endl; + os.close (); + + // tests/build/.gitignore + // + if (v == vcs::git) + { + os.open (f = tbd / ".gitignore"); + os << "config.build" << endl + << "root/" << endl + << "bootstrap/" << endl; + os.close (); + } + + // tests/buildfile + // + os.open (f = td / "buildfile"); + os << "./: {*/ -build/}" << endl; + os.close (); + + // tests/.gitignore + // + if (v == vcs::git) + { + os.open (f = td / ".gitignore"); + os << "# Test executables." << endl + << "#" << endl + << "driver" << endl + << endl + << "# Testscript output directories (can be symlinks)." << endl + << "#" << endl + << "test" << endl + << "test-*" << endl; + os.close (); + } + + // tests/basics/ + // + td /= "basics"; + mk (td); + + switch (l) + { + case lang::c: + { + // tests/basics/driver.c + // + os.open (f = td / "driver.c"); + os << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << endl + << "#include <" << n << "/" << ver << ">" << endl + << "#include <" << n << "/" << hdr << ">" << endl + << endl + << "int main ()" << endl + << "{" << endl + << " char b[256];" << endl + << endl + << " // Basics." << endl + << " //" << endl + << " {" << endl + << " FILE *o = fmemopen (b, sizeof (b), \"w\");" << endl + << " assert (say_hello (o, \"World\") > 0);" << endl + << " fclose (o);" << endl + << " assert (strcmp (b, \"Hello, World!\\n\") == 0);" << endl + << " }" << endl + << endl + << " // Empty name." << endl + << " //" << endl + << " {" << endl + << " FILE *o = fmemopen (b, sizeof (b), \"w\");" << endl + << " assert (say_hello (o, \"\") < 0 && errno == EINVAL);" << endl + << " fclose (o);" << endl + << " }" << endl + << endl + << " return 0;" << endl + << "}" << endl; + os.close (); + + break; + } + case lang::cxx: + { + string x (l.cxx_opt.cpp () ? "pp" : "xx"); + + // tests/basics/driver.c(xx|pp) + // + os.open (f = td / "driver.c" + x); + os << "#include " << endl + << "#include " << endl + << "#include " << endl + << endl + << "#include <" << n << "/" << ver << ">" << endl + << "#include <" << n << "/" << hdr << ">" << endl + << endl + << "using namespace std;" << endl + << "using namespace " << s << ";" << endl + << endl + << "int main ()" << endl + << "{" << endl + << " // Basics." << endl + << " //" << endl + << " {" << endl + << " ostringstream o;" << endl + << " say_hello (o, \"World\");" << endl + << " assert (o.str () == \"Hello, World!\\n\");" << endl + << " }" << endl + << endl + << " // Empty name." << endl + << " //" << endl + << " try" << endl + << " {" << endl + << " ostringstream o;" << endl + << " say_hello (o, \"\");" << endl + << " assert (false);" << endl + << " }" << endl + << " catch (const invalid_argument& e)" << endl + << " {" << endl + << " assert (e.what () == string (\"empty name\"));" << endl + << " }" << endl + << "}" << endl; + os.close (); + + break; + } + } + + // tests/basics/buildfile + // + os.open (f = td / "buildfile"); + os << "import libs = " << n << "%lib{" << s << "}" << endl + << endl; + + switch (l) + { + case lang::c: + { + os << "exe{driver}: {h c}{*} $libs" << endl; + break; + } + case lang::cxx: + { + os << "exe{driver}: {hxx ixx txx cxx}{*} $libs" << endl; + break; + } + } + //os << endl + // << x << ".poptions =+ \"-I$out_root\" \"-I$src_root\"" << endl; + os.close (); + break; } case type::bare: -- cgit v1.1