From df5e58e6e5eb2727a185bf9a98a462c18fa3a83d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 8 Mar 2018 18:06:26 +0200 Subject: Setup project database infrastructure --- bdep/.gitignore | 1 + bdep/bdep.cxx | 8 ++--- bdep/buildfile | 34 ++++++++++++++----- bdep/config.cli | 4 +-- bdep/configuration.cli | 35 -------------------- bdep/database.cxx | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ bdep/database.hxx | 82 +++++++++++++++++++++++++++++++++++++++++++++ bdep/diagnostics.cxx | 25 ++++++++++++++ bdep/diagnostics.hxx | 18 +++++++++- bdep/init.cli | 4 +-- bdep/odb.sh | 18 ++++++++++ bdep/project.cli | 20 ++++++++++- bdep/project.hxx | 37 +++++++++++++++++++++ bdep/project.xml | 11 +++++++ bdep/utility.cxx | 2 ++ bdep/utility.hxx | 2 ++ bdep/version.hxx.in | 8 +++++ bdep/wrapper-traits.hxx | 59 +++++++++++++++++++++++++++++++++ manifest | 2 ++ 19 files changed, 404 insertions(+), 54 deletions(-) delete mode 100644 bdep/configuration.cli create mode 100644 bdep/database.cxx create mode 100644 bdep/database.hxx create mode 100755 bdep/odb.sh create mode 100644 bdep/project.xml create mode 100644 bdep/wrapper-traits.hxx diff --git a/bdep/.gitignore b/bdep/.gitignore index 15d9eb3..02a0554 100644 --- a/bdep/.gitignore +++ b/bdep/.gitignore @@ -1,3 +1,4 @@ bdep +*-odb.?xx *-options.?xx version.hxx diff --git a/bdep/bdep.cxx b/bdep/bdep.cxx index ca6ff64..77dcd71 100644 --- a/bdep/bdep.cxx +++ b/bdep/bdep.cxx @@ -16,7 +16,7 @@ #include #include -#include +#include // Commands. // @@ -33,11 +33,11 @@ using namespace bdep; // Once this is done, use the "final" values of the common options to do // global initializations (verbosity level, etc). // -// If O is-a configuration_options, then also handle the @ arguments -// and place them into configuration_options::config_name. +// If O is-a project_options, then also handle the @ arguments and +// place them into project_options::config_name. // static inline bool -cfg_name (configuration_options* o, const char* a) +cfg_name (project_options* o, const char* a) { string n (a); diff --git a/bdep/buildfile b/bdep/buildfile index 5ef9099..cd7c690 100644 --- a/bdep/buildfile +++ b/bdep/buildfile @@ -2,25 +2,41 @@ # copyright : Copyright (c) 2014-2017 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file +# @@ ODB: these are ODB changelogs that are both generated and stored in the +# repository (what if src != out?). Will need to think how to handle +# them properly (always generate in src_base?). +# +define xml: file +xml{*}: extension = xml + import libs = libbpkg%lib{bpkg} import libs += libbutl%lib{butl} +import libs += libodb%lib{odb} +import libs += libodb-sqlite%lib{odb-sqlite} -options_topics = \ -bdep-options \ -common-options \ -project-options \ -configuration-options \ -help-options \ -config-options \ +options_topics = \ +bdep-options \ +common-options \ +project-options \ +help-options \ +config-options \ init-options -exe{bdep}: {hxx ixx txx cxx}{** -{$options_topics} -version} \ - {hxx ixx cxx}{$options_topics} {hxx}{version} $libs +exe{bdep}: {hxx ixx txx cxx}{** -{$options_topics} -*-odb -version} \ + {hxx ixx cxx}{$options_topics} {hxx ixx cxx}{project-odb} \ + {hxx}{version} $libs hxx{version}: in{version} $src_root/file{manifest} obj{utility}: cxx.poptions += -DBDEP_EXE_SUFFIX='"'$bin.exe.suffix'"' +# Disable "unknown pragma" warnings. +# +if ($cxx.class == 'msvc') + cxx.coptions += /wd4068 +elif ($cxx.class == 'gcc') + cxx.coptions += -Wno-unknown-pragmas + if $cli.configured { # General topics and common options. diff --git a/bdep/config.cli b/bdep/config.cli index 42a0fe6..434dfc2 100644 --- a/bdep/config.cli +++ b/bdep/config.cli @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -include ; +include ; "\section=1" "\name=bdep-config" @@ -34,7 +34,7 @@ namespace bdep // Note that not all project/configuration options are valid for all // subcommands. // - class cmd_config_options: configuration_options + class cmd_config_options: project_options { "\h|CONFIG OPTIONS|" diff --git a/bdep/configuration.cli b/bdep/configuration.cli deleted file mode 100644 index e6e911a..0000000 --- a/bdep/configuration.cli +++ /dev/null @@ -1,35 +0,0 @@ -// file : bdep/configuration.cli -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -include ; - -"\name=configuration" // Not a man page. - -namespace bdep -{ - // Common options for commands that operate on configurations (cfg-spec). - // Note that all of them also operate on project/packages thus inheritance - // from project_options. - // - class configuration_options: project_options - { - dir_paths --config|-c - { - "", - "Specify the build configuration to use as a directory." - } - - bool --all|-a - { - "Use all build configurations." - } - - // Storage for configuration names specified as @. - // - // Note that we leave it undocumented so that it's not mentioned in - // documentation. - // - strings --config-name; - }; -} diff --git a/bdep/database.cxx b/bdep/database.cxx new file mode 100644 index 0000000..c456961 --- /dev/null +++ b/bdep/database.cxx @@ -0,0 +1,88 @@ +// file : bdep/database.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include + +#include + +using namespace std; + +namespace bdep +{ + using namespace odb::sqlite; + using odb::schema_catalog; + + database + open (const dir_path& d, tracer& tr, bool create) + { + tracer trace ("open"); + + path f (d / bdep_dir / "bdep.sqlite3"); + + if (!create && !exists (f)) + fail << d << " does not look like an initialized project directory" << + info << "run 'bdep init' to initialize"; + + try + { + // We don't need the thread pool. + // + unique_ptr cf (new single_connection_factory); + + database db (f.string (), + SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), + true, // Enable FKs. + "", // Default VFS. + move (cf)); + + db.tracer (trace); + + // Lock the database for as long as the connection is active. First we + // set locking_mode to EXCLUSIVE which instructs SQLite not to release + // any locks until the connection is closed. Then we force SQLite to + // acquire the write lock by starting exclusive transaction. See the + // locking_mode pragma documentation for details. This will also fail if + // the database is inaccessible (e.g., file does not exist, already used + // by another process, etc). + // + try + { + db.connection ()->execute ("PRAGMA locking_mode = EXCLUSIVE"); + transaction t (db.begin_exclusive ()); + + if (create) + { + // Create the new schema. + // + if (db.schema_version () != 0) + fail << f << ": already has database schema"; + + schema_catalog::create_schema (db); + } + else + { + // Migrate the database if necessary. + // + schema_catalog::migrate (db); + } + + t.commit (); + } + catch (odb::timeout&) + { + fail << "project " << d << " is already used by another process"; + } + + db.tracer (tr); // Switch to the caller's tracer. + return db; + } + catch (const database_exception& e) + { + fail << f << ": " << e.message () << endf; + } + } +} diff --git a/bdep/database.hxx b/bdep/database.hxx new file mode 100644 index 0000000..76642bf --- /dev/null +++ b/bdep/database.hxx @@ -0,0 +1,82 @@ +// file : bdep/database.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BDEP_DATABASE_HXX +#define BDEP_DATABASE_HXX + +#include // remove_reference + +#include +#include +#include + +#include + +#include +#include + +#include + +namespace bdep +{ + using odb::query; + using odb::result; + using odb::session; + + using odb::sqlite::database; + using odb::sqlite::transaction; + + database + open (const dir_path& project, tracer&, bool create = false); + + struct tracer_guard + { + tracer_guard (database& db, tracer& t) + : db_ (db), t_ (db.tracer ()) {db.tracer (t);} + ~tracer_guard () {db_.tracer (*t_);} + + private: + database& db_; + odb::tracer* t_; + }; + + // Range-based for-loop iteration over query result that returns object + // pointers. For example: + // + // for (shared_ptr o: pointer_result (db.query (...))) + // + template + class pointer_result_range + { + R r_; + + public: + pointer_result_range (R&& r): r_ (forward (r)) {} + + using base_iterator = typename std::remove_reference::type::iterator; + + struct iterator: base_iterator + { + iterator () = default; + + explicit + iterator (base_iterator i): base_iterator (move (i)) {} + + typename base_iterator::pointer_type + operator* () {return this->load ();} + }; + + iterator begin () {return iterator (r_.begin ());} + iterator end () {return iterator (r_.end ());} + }; + + template + inline pointer_result_range + pointer_result (R&& r) + { + return pointer_result_range (forward (r)); + } +} + +#endif // BDEP_DATABASE_HXX diff --git a/bdep/diagnostics.cxx b/bdep/diagnostics.cxx index f596e49..cd77a91 100644 --- a/bdep/diagnostics.cxx +++ b/bdep/diagnostics.cxx @@ -4,6 +4,8 @@ #include +#include + #include #include // operator<<(ostream, process_arg) @@ -58,6 +60,29 @@ namespace bdep r << name_ << ": "; } + // trace + // + void trace_mark_base:: + prepare (odb::connection&, const odb::statement& s) + { + if (verb >= 6) + static_cast (*this) << "PREPARE " << s.text (); + } + + void trace_mark_base:: + execute (odb::connection&, const char* stmt) + { + if (verb >= 5) + static_cast (*this) << stmt; + } + + void trace_mark_base:: + deallocate (odb::connection&, const odb::statement& s) + { + if (verb >= 6) + static_cast (*this) << "DEALLOCATE " << s.text (); + } + // tracer // void tracer:: diff --git a/bdep/diagnostics.hxx b/bdep/diagnostics.hxx index 366b6c0..9a233f0 100644 --- a/bdep/diagnostics.hxx +++ b/bdep/diagnostics.hxx @@ -5,6 +5,8 @@ #ifndef BDEP_DIAGNOSTICS_HXX #define BDEP_DIAGNOSTICS_HXX +#include + #include #include // Note: not @@ -176,11 +178,25 @@ namespace bdep // trace // - struct trace_mark_base: basic_mark_base + // Also implement the ODB tracer interface so that we can use it to trace + // database statement execution. + // + struct trace_mark_base: basic_mark_base, odb::tracer { explicit trace_mark_base (const char* name, const void* data = nullptr) : basic_mark_base ("trace", name, data) {} + + // odb::tracer interface. + // + virtual void + prepare (odb::connection&, const odb::statement&); + + virtual void + execute (odb::connection&, const char* statement); + + virtual void + deallocate (odb::connection&, const odb::statement&); }; using trace_mark = butl::diag_mark; diff --git a/bdep/init.cli b/bdep/init.cli index 7eeeb6b..6e50f10 100644 --- a/bdep/init.cli +++ b/bdep/init.cli @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -include ; +include ; "\section=1" "\name=bdep-init" @@ -57,7 +57,7 @@ namespace bdep " } - class cmd_init_options: configuration_options + class cmd_init_options: project_options { "\h|INIT OPTIONS|" diff --git a/bdep/odb.sh b/bdep/odb.sh new file mode 100755 index 0000000..77372b9 --- /dev/null +++ b/bdep/odb.sh @@ -0,0 +1,18 @@ +#! /usr/bin/env bash + +trap 'exit 1' ERR + +odb=odb +lib="\ +-I$HOME/work/odb/builds/default/libodb-sqlite-default \ +-I$HOME/work/odb/libodb-sqlite \ +-I$HOME/work/odb/builds/default/libodb-default \ +-I$HOME/work/odb/libodb" + +$odb $lib -I.. -I../../libbpkg -I../../libbutl \ + -DLIBODB_BUILD2 -DLIBODB_SQLITE_BUILD2 --generate-schema \ + -d sqlite --std c++11 --generate-query \ + --odb-epilogue '#include ' \ + --hxx-prologue '#include ' \ + --include-with-brackets --include-prefix bdep --guard-prefix BDEP \ + --sqlite-override-null project.hxx diff --git a/bdep/project.cli b/bdep/project.cli index 7b6af76..f080917 100644 --- a/bdep/project.cli +++ b/bdep/project.cli @@ -9,10 +9,28 @@ include ; namespace bdep { // Common options for commands that operate on project/packages (prj-spec - // and pkg-spec). + // and pkg-spec) and configurations (cfg-spec). // class project_options: common_options { + dir_paths --config|-c + { + "", + "Specify the build configuration to use as a directory." + } + + bool --all|-a + { + "Use all build configurations." + } + + // Storage for configuration names specified as @. + // + // Note that we leave it undocumented so that it's not mentioned in + // documentation. + // + strings --config-name; + dir_paths --directory|-d { "", diff --git a/bdep/project.hxx b/bdep/project.hxx index 6a748d5..274455c 100644 --- a/bdep/project.hxx +++ b/bdep/project.hxx @@ -5,13 +5,50 @@ #ifndef BDEP_PROJECT_HXX #define BDEP_PROJECT_HXX +#include + #include #include #include +#pragma db model version(1, 1, open) + +// Prevent assert() macro expansion in get/set expressions. This should appear +// after all #include directives since the assert() macro is redefined in each +// inclusion. +// +#ifdef ODB_COMPILER +# undef assert +# define assert assert +void assert (int); +#endif + namespace bdep { + #pragma db map type(dir_path) as(string) \ + to((?).string ()) from(bdep::dir_path (?)) + + //@@ TODO: do we need session/shared_ptr? + + #pragma db object pointer(shared_ptr) session + class configuration + { + public: + + dir_path path; // @@ TODO: relative or absolute? + string name; + + // Database mapping. + // + #pragma db member(name) id + //#pragma db member(name) index + + private: + friend class odb::access; + configuration () = default; + }; + // 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 diff --git a/bdep/project.xml b/bdep/project.xml new file mode 100644 index 0000000..c3991bc --- /dev/null +++ b/bdep/project.xml @@ -0,0 +1,11 @@ + + + + + + + + +
+
+
diff --git a/bdep/utility.cxx b/bdep/utility.cxx index edfdc41..93f0458 100644 --- a/bdep/utility.cxx +++ b/bdep/utility.cxx @@ -20,6 +20,8 @@ namespace bdep const path empty_path; const dir_path empty_dir_path; + const dir_path bdep_dir (".bdep"); + const path manifest_file ("manifest"); const path packages_file ("packages.manifest"); const path configurations_file ("configurations.manifest"); diff --git a/bdep/utility.hxx b/bdep/utility.hxx index 8bca172..54e17fb 100644 --- a/bdep/utility.hxx +++ b/bdep/utility.hxx @@ -56,6 +56,8 @@ namespace bdep // Widely-used paths. // + extern const dir_path bdep_dir; // .bdep/ + extern const path manifest_file; // manifest extern const path packages_file; // packages.manifest extern const path configurations_file; // configurations.manifest diff --git a/bdep/version.hxx.in b/bdep/version.hxx.in index c243114..73b2df9 100644 --- a/bdep/version.hxx.in +++ b/bdep/version.hxx.in @@ -45,4 +45,12 @@ $libbutl.check(LIBBUTL_VERSION, LIBBUTL_SNAPSHOT)$ $libbpkg.check(LIBBPKG_VERSION, LIBBPKG_SNAPSHOT)$ +#include + +$libodb.check(LIBODB_VERSION, LIBODB_SNAPSHOT)$ + +#include + +$libodb-sqlite.check(LIBODB_SQLITE_VERSION, LIBODB_SQLITE_SNAPSHOT)$ + #endif // BDEP_VERSION diff --git a/bdep/wrapper-traits.hxx b/bdep/wrapper-traits.hxx new file mode 100644 index 0000000..80b0cff --- /dev/null +++ b/bdep/wrapper-traits.hxx @@ -0,0 +1,59 @@ +// file : bdep/wrapper-traits.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BDEP_WRAPPER_TRAITS_HXX +#define BDEP_WRAPPER_TRAITS_HXX + +#include + +#include + +namespace odb +{ + template + class wrapper_traits> + { + public: + typedef T wrapped_type; + typedef butl::optional wrapper_type; + + // T can be const. + // + typedef + typename odb::details::meta::remove_const::result + unrestricted_wrapped_type; + + static const bool null_handler = true; + static const bool null_default = true; + + static bool + get_null (const wrapper_type& o) + { + return !o; + } + + static void + set_null (wrapper_type& o) + { + o = wrapper_type (); + } + + static const wrapped_type& + get_ref (const wrapper_type& o) + { + return *o; + } + + static unrestricted_wrapped_type& + set_ref (wrapper_type& o) + { + if (!o) + o = unrestricted_wrapped_type (); + + return const_cast (*o); + } + }; +} + +#endif // BDEP_WRAPPER_TRAITS_HXX diff --git a/manifest b/manifest index debfdce..c7eb306 100644 --- a/manifest +++ b/manifest @@ -16,5 +16,7 @@ depends: * build2 >= 0.7.0- depends: * bpkg >= 0.7.0- # @@ Should probably become conditional dependency. requires: ? cli ; Only required if changing .cli files. +depends: libodb [2.5.0-b.6.1 2.5.0-b.7) +depends: libodb-sqlite [2.5.0-b.6.1 2.5.0-b.7) depends: libbutl [0.7.0-a.0.1 0.7.0-a.1) depends: libbpkg [0.7.0-a.0.1 0.7.0-a.1) -- cgit v1.1