aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bdep/configuration.cli2
-rw-r--r--bdep/diagnostics.cxx2
-rw-r--r--bdep/diagnostics.hxx7
-rw-r--r--bdep/init.cli17
-rw-r--r--bdep/init.cxx14
-rw-r--r--bdep/project.cxx200
-rw-r--r--bdep/project.hxx33
-rw-r--r--bdep/utility.cxx8
-rw-r--r--bdep/utility.hxx60
-rw-r--r--bdep/utility.txx112
10 files changed, 436 insertions, 19 deletions
diff --git a/bdep/configuration.cli b/bdep/configuration.cli
index 190845c..e6e911a 100644
--- a/bdep/configuration.cli
+++ b/bdep/configuration.cli
@@ -16,7 +16,7 @@ namespace bdep
{
dir_paths --config|-c
{
- "<cfg-dir>",
+ "<dir>",
"Specify the build configuration to use as a directory."
}
diff --git a/bdep/diagnostics.cxx b/bdep/diagnostics.cxx
index e60ebc8..f596e49 100644
--- a/bdep/diagnostics.cxx
+++ b/bdep/diagnostics.cxx
@@ -7,6 +7,8 @@
#include <libbutl/process.mxx>
#include <libbutl/process-io.mxx> // operator<<(ostream, process_arg)
+#include <bdep/utility.hxx>
+
using namespace std;
using namespace butl;
diff --git a/bdep/diagnostics.hxx b/bdep/diagnostics.hxx
index 8c3b3d2..366b6c0 100644
--- a/bdep/diagnostics.hxx
+++ b/bdep/diagnostics.hxx
@@ -7,8 +7,7 @@
#include <libbutl/diagnostics.mxx>
-#include <bdep/types.hxx>
-#include <bdep/utility.hxx>
+#include <bdep/types.hxx> // Note: not <bdep/utility.hxx>
namespace bdep
{
@@ -157,7 +156,9 @@ namespace bdep
epilogue_,
type_,
name_,
- location (forward<F> (f), forward<L> (l), forward<C> (c)));
+ location (std::forward<F> (f),
+ std::forward<L> (l),
+ std::forward<C> (c)));
}
protected:
diff --git a/bdep/init.cli b/bdep/init.cli
index 3861b8b..7eeeb6b 100644
--- a/bdep/init.cli
+++ b/bdep/init.cli
@@ -60,5 +60,22 @@ namespace bdep
class cmd_init_options: configuration_options
{
"\h|INIT OPTIONS|"
+
+ bool --empty|-E
+ {
+ "Initialize an empty build configuration set."
+ }
+
+ dir_path --config-add|-A
+ {
+ "<dir>",
+ "Add an existing build configuration <dir>."
+ }
+
+ dir_path --config-create|-C
+ {
+ "<dir>",
+ "Create a new build configuration in <dir>."
+ }
};
}
diff --git a/bdep/init.cxx b/bdep/init.cxx
index 3e7998f..1e850b8 100644
--- a/bdep/init.cxx
+++ b/bdep/init.cxx
@@ -4,6 +4,7 @@
#include <bdep/init.hxx>
+#include <bdep/project.hxx>
#include <bdep/diagnostics.hxx>
using namespace std;
@@ -15,10 +16,17 @@ namespace bdep
{
tracer trace ("init");
- //@@ TODO: validate project/config options for subcommands.
+ //@@ TODO: validate project/config options for sub-modes.
+ //@@ TODO: print project/package(s) being initialized.
- for (const string& n: o.config_name ())
- text << n;
+ project_packages pp (
+ find_project_packages (o,
+ o.empty () /* ignore_packages */));
+
+ text << pp.project;
+
+ for (const dir_path& d: pp.packages)
+ text << " " << (pp.project / d);
return 0;
}
diff --git a/bdep/project.cxx b/bdep/project.cxx
new file mode 100644
index 0000000..b7c1775
--- /dev/null
+++ b/bdep/project.cxx
@@ -0,0 +1,200 @@
+// file : bdep/project.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bdep/project.hxx>
+
+#include <libbpkg/manifest.hxx>
+
+#include <bdep/diagnostics.hxx>
+
+using namespace std;
+
+namespace bdep
+{
+ // Given a directory which can a project root, a package root, or one of
+ // their subdirectories, return the absolute project (first) and relative
+ // package (second) directories. The package directory may be absent if the
+ // given directory is not within a package root or empty if the project and
+ // package roots are the same.
+ //
+ struct project_package
+ {
+ dir_path project;
+ optional<dir_path> package;
+ };
+
+ static project_package
+ find_project_packages (const dir_path& start)
+ {
+ dir_path prj;
+ optional<dir_path> pkg;
+
+ dir_path d (start);
+ d.complete ();
+ d.normalize ();
+ for (; !d.empty (); d = d.directory ())
+ {
+ // Ignore errors when checking for file existence since we may be
+ // iterating over directories past any reasonable project boundaries.
+ //
+ if (exists (d / manifest_file, true))
+ {
+ if (pkg)
+ {
+ fail << "multiple package manifests between " << start
+ << " and project root" <<
+ info << "first manifest is in " << *pkg <<
+ info << "second manifest is in " << d;
+ }
+
+ pkg = d;
+
+ // Fall through (can also be the project root).
+ }
+
+ // Check for configurations.manifest first since a simple project will
+ // have no packages.manifest
+ //
+ if (exists (d / configurations_file, true) ||
+ exists (d / packages_file, true))
+ {
+ prj = move (d);
+ break;
+ }
+ }
+
+ if (prj.empty ())
+ {
+ if (!pkg)
+ fail << start << " is no a (sub)directory of a package or project";
+
+ // Project and package are the same.
+ //
+ prj = move (*pkg);
+ pkg = dir_path ();
+ }
+ else if (pkg)
+ pkg = pkg->leaf (prj);
+
+ return project_package {move (prj), move (pkg)};
+ }
+
+ project_packages
+ find_project_packages (const project_options& po, bool ignore_packages)
+ {
+ project_packages r;
+
+ if (po.directory_specified ())
+ {
+ for (const dir_path& d: po.directory ())
+ {
+ project_package p (find_project_packages (d));
+
+ // We only work on one project at a time.
+ //
+ if (r.project.empty ())
+ {
+ r.project = move (p.project);
+ }
+ else if (r.project != p.project)
+ {
+ fail << "multiple project directories specified" <<
+ info << r.project <<
+ info << p.project;
+ }
+
+ if (!ignore_packages && p.package)
+ {
+ // Suppress duplicate packages.
+ //
+ if (find (r.packages.begin (),
+ r.packages.end (),
+ *p.package) == r.packages.end ())
+ {
+ r.packages.push_back (move (*p.package));
+ }
+ }
+ }
+ }
+ else
+ {
+ project_package p (find_project_packages (path::current_directory ()));
+
+ r.project = move (p.project);
+
+ if (!ignore_packages && p.package)
+ {
+ r.packages.push_back (move (*p.package));
+ }
+ }
+
+ if (!ignore_packages)
+ {
+ // If exists, load packages.manifest from the project root and either
+ // verify that the discovered packages are in it or, if nothing was
+ // discovered, use it as the source for the package list.
+ //
+ path f (r.project / packages_file);
+
+ if (exists (f))
+ {
+ using bpkg::package_manifest;
+ using bpkg::dir_package_manifests;
+
+ auto ms (parse_manifest<dir_package_manifests> (f, "packages"));
+
+ // While an empty repository is legal, in our case it doesn't make
+ // much sense and will just further complicate things.
+ //
+ if (ms.empty ())
+ fail << "no packages listed in " << f;
+
+ // Convert the package location from POSIX to the host form and make
+ // sure the current directory is represented as an empty path.
+ //
+ auto location = [] (const package_manifest& m)
+ {
+ assert (m.location);
+ dir_path d (path_cast<dir_path> (*m.location));
+ d.normalize (false /* actualize */, true /* cur_empty */);
+ return d;
+ };
+
+ if (r.packages.empty ())
+ {
+ for (package_manifest& m: ms)
+ r.packages.push_back (location (m));
+ }
+ else
+ {
+ // It could be costly to normalize the location for each
+ // comparison. We, however, do not expect more than a handful of
+ // packages so we are probably ok.
+ //
+ for (const dir_path& pd: r.packages)
+ {
+ if (find_if (ms.begin (),
+ ms.end (),
+ [&pd, &location] (const package_manifest& m)
+ {
+ return pd == location (m);
+ }) == ms.end ())
+ {
+ fail << "package directory " << pd << " not listed in " << f;
+ }
+ }
+ }
+ }
+ else
+ {
+ // If packages.manifest does not exist, then this must be a simple
+ // project.
+ //
+ assert (r.packages.size () == 1 && r.packages[0].empty ());
+ }
+ }
+
+ return r;
+ }
+}
diff --git a/bdep/project.hxx b/bdep/project.hxx
new file mode 100644
index 0000000..6a748d5
--- /dev/null
+++ b/bdep/project.hxx
@@ -0,0 +1,33 @@
+// file : bdep/project.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BDEP_PROJECT_HXX
+#define BDEP_PROJECT_HXX
+
+#include <bdep/types.hxx>
+#include <bdep/utility.hxx>
+
+#include <bdep/project-options.hxx>
+
+namespace bdep
+{
+ // Given project_options (and CWD) locate the packages and their project.
+ // The result is the absolute and normalized project directory and a vector
+ // of relative (to the project directory) package directories (which will be
+ // empty if ignore_packages is true).
+ //
+ // Note that if the package directory is the same as project, then the
+ // package directory will be empty (and not ./).
+ //
+ struct project_packages
+ {
+ dir_path project;
+ dir_paths packages;
+ };
+
+ project_packages
+ find_project_packages (const project_options&, bool ignore_packages = false);
+}
+
+#endif // BDEP_PROJECT_HXX
diff --git a/bdep/utility.cxx b/bdep/utility.cxx
index cb005a0..edfdc41 100644
--- a/bdep/utility.cxx
+++ b/bdep/utility.cxx
@@ -16,10 +16,14 @@ using namespace butl;
namespace bdep
{
- const string empty_string;
- const path empty_path;
+ const string empty_string;
+ const path empty_path;
const dir_path empty_dir_path;
+ const path manifest_file ("manifest");
+ const path packages_file ("packages.manifest");
+ const path configurations_file ("configurations.manifest");
+
bool
exists (const path& f, bool ignore_error)
{
diff --git a/bdep/utility.hxx b/bdep/utility.hxx
index 48f168f..8bca172 100644
--- a/bdep/utility.hxx
+++ b/bdep/utility.hxx
@@ -5,11 +5,12 @@
#ifndef BDEP_UTILITY_HXX
#define BDEP_UTILITY_HXX
-#include <memory> // make_shared()
-#include <string> // to_string()
-#include <utility> // move(), forward(), declval(), make_pair()
-#include <cassert> // assert()
-#include <iterator> // make_move_iterator()
+#include <memory> // make_shared()
+#include <string> // to_string()
+#include <utility> // move(), forward(), declval(), make_pair()
+#include <cassert> // assert()
+#include <iterator> // make_move_iterator()
+#include <algorithm> // find(), find_if()
#include <libbutl/ft/lang.hxx>
@@ -31,6 +32,9 @@ namespace bdep
using std::make_move_iterator;
using std::to_string;
+ using std::find;
+ using std::find_if;
+
// <libbutl/utility.mxx>
//
using butl::casecmp;
@@ -46,10 +50,21 @@ namespace bdep
// Empty string and path.
//
- extern const string empty_string;
- extern const path empty_path;
+ extern const string empty_string;
+ extern const path empty_path;
extern const dir_path empty_dir_path;
+ // Widely-used paths.
+ //
+ extern const path manifest_file; // manifest
+ extern const path packages_file; // packages.manifest
+ extern const path configurations_file; // configurations.manifest
+
+ // Directory extracted from argv[0] (i.e., this process' recall directory)
+ // or empty if there is none. Can be used as a search fallback.
+ //
+ extern dir_path exec_dir;
+
// Filesystem.
//
bool
@@ -70,10 +85,35 @@ namespace bdep
void
rm (const path&, uint16_t verbosity = 3);
- // Directory extracted from argv[0] (i.e., this process' recall directory)
- // or empty if there is none. Can be used as a search fallback.
+ // Manifest parsing and serialization.
//
- extern dir_path exec_dir;
+ // For parsing, if path is '-', then read from stdin.
+ //
+ template <typename T>
+ T
+ parse_manifest (const path&,
+ const char* what,
+ bool ignore_unknown = false);
+
+ template <typename T>
+ T
+ parse_manifest (istream&,
+ const string& name,
+ const char* what,
+ bool ignore_unknown = false);
+
+ template <typename T>
+ void
+ serialize_manifest (const T&, const path&, const char* what);
+
+ template <typename T>
+ void
+ serialize_manifest (const T&,
+ ostream&,
+ const string& name,
+ const char* what);
}
+#include <bdep/utility.txx>
+
#endif // BDEP_UTILITY_HXX
diff --git a/bdep/utility.txx b/bdep/utility.txx
new file mode 100644
index 0000000..38f0e35
--- /dev/null
+++ b/bdep/utility.txx
@@ -0,0 +1,112 @@
+// file : bdep/utility.txx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <iostream> // cin
+
+#include <libbutl/fdstream.mxx>
+
+#include <libbutl/manifest-parser.mxx>
+#include <libbutl/manifest-serializer.mxx>
+
+#include <bdep/diagnostics.hxx>
+
+namespace bdep
+{
+ // *_manifest()
+ //
+ template <typename T>
+ T
+ parse_manifest (const path& f, const char* what, bool iu)
+ {
+ using namespace butl;
+
+ try
+ {
+ if (f.string () == "-")
+ return parse_manifest<T> (std::cin, "stdin", what, iu);
+
+ if (!file_exists (f))
+ fail << what << " manifest file " << f << " does not exist";
+
+ ifdstream ifs (f);
+ return parse_manifest<T> (ifs, f.string (), what, iu);
+ }
+ catch (const system_error& e) // EACCES, etc.
+ {
+ fail << "unable to access " << what << " manifest " << f << ": " << e
+ << endf;
+ }
+ }
+
+ template <typename T>
+ T
+ parse_manifest (istream& is, const string& name, const char* what, bool iu)
+ {
+ using namespace butl;
+
+ try
+ {
+ manifest_parser p (is, name);
+ return T (p, iu);
+ }
+ catch (const manifest_parsing& e)
+ {
+ fail << "invalid " << what << " manifest: " << name << ':'
+ << e.line << ':' << e.column << ": " << e.description << endf;
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to read " << what << " manifest " << name << ": " << e
+ << endf;
+ }
+ }
+
+ template <typename T>
+ void
+ serialize_manifest (const T& m, const path& f, const char* what)
+ {
+ using namespace std;
+ using namespace butl;
+
+ try
+ {
+ ofdstream ofs (f, ios::binary);
+ auto_rmfile arm (f); // Try to remove on failure ignoring errors.
+
+ serialize_manifest (m, ofs, f.string (), what);
+
+ ofs.close ();
+ arm.cancel ();
+ }
+ catch (const system_error& e) // EACCES, etc.
+ {
+ fail << "unable to access " << what << " manifest " << f << ": " << e;
+ }
+ }
+
+ template <typename T>
+ void
+ serialize_manifest (const T& m,
+ ostream& os,
+ const string& name,
+ const char* what)
+ {
+ using namespace butl;
+
+ try
+ {
+ manifest_serializer s (os, name);
+ m.serialize (s);
+ return;
+ }
+ catch (const manifest_serialization& e)
+ {
+ fail << "invalid " << what << " manifest: " << e.description;
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to write " << what << " manifest " << name << ": " << e;
+ }
+ }
+}