diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2018-03-08 18:06:26 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2018-03-08 18:06:26 +0200 |
commit | df5e58e6e5eb2727a185bf9a98a462c18fa3a83d (patch) | |
tree | fb0cc76643865390ac3e45462d51ea02575a3175 | |
parent | f9ebe2d1e920df001be2dd543a63677f8728f53d (diff) |
Setup project database infrastructure
-rw-r--r-- | bdep/.gitignore | 1 | ||||
-rw-r--r-- | bdep/bdep.cxx | 8 | ||||
-rw-r--r-- | bdep/buildfile | 34 | ||||
-rw-r--r-- | bdep/config.cli | 4 | ||||
-rw-r--r-- | bdep/configuration.cli | 35 | ||||
-rw-r--r-- | bdep/database.cxx | 88 | ||||
-rw-r--r-- | bdep/database.hxx | 82 | ||||
-rw-r--r-- | bdep/diagnostics.cxx | 25 | ||||
-rw-r--r-- | bdep/diagnostics.hxx | 18 | ||||
-rw-r--r-- | bdep/init.cli | 4 | ||||
-rwxr-xr-x | bdep/odb.sh | 18 | ||||
-rw-r--r-- | bdep/project.cli | 20 | ||||
-rw-r--r-- | bdep/project.hxx | 37 | ||||
-rw-r--r-- | bdep/project.xml | 11 | ||||
-rw-r--r-- | bdep/utility.cxx | 2 | ||||
-rw-r--r-- | bdep/utility.hxx | 2 | ||||
-rw-r--r-- | bdep/version.hxx.in | 8 | ||||
-rw-r--r-- | bdep/wrapper-traits.hxx | 59 | ||||
-rw-r--r-- | manifest | 2 |
19 files changed, 404 insertions, 54 deletions
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 <bdep/diagnostics.hxx> #include <bdep/bdep-options.hxx> -#include <bdep/config-options.hxx> +#include <bdep/project-options.hxx> // 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 @<cfg-name> arguments -// and place them into configuration_options::config_name. +// If O is-a project_options, then also handle the @<cfg-name> 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 <bdep/configuration.cli>; +include <bdep/project.cli>; "\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 <bdep/project.cli>; - -"\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 - { - "<dir>", - "Specify the build configuration to use as a directory." - } - - bool --all|-a - { - "Use all build configurations." - } - - // Storage for configuration names specified as @<cfg-name>. - // - // 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 <bdep/database.hxx> + +#include <odb/schema-catalog.hxx> +#include <odb/sqlite/exceptions.hxx> + +#include <bdep/diagnostics.hxx> + +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<connection_factory> 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 <type_traits> // remove_reference + +#include <odb/query.hxx> +#include <odb/result.hxx> +#include <odb/session.hxx> + +#include <odb/sqlite/database.hxx> + +#include <bdep/types.hxx> +#include <bdep/utility.hxx> + +#include <bdep/diagnostics.hxx> + +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<object> o: pointer_result (db.query<object> (...))) + // + template <typename R> + class pointer_result_range + { + R r_; + + public: + pointer_result_range (R&& r): r_ (forward<R> (r)) {} + + using base_iterator = typename std::remove_reference<R>::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 <typename R> + inline pointer_result_range<R> + pointer_result (R&& r) + { + return pointer_result_range<R> (forward<R> (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 <bdep/diagnostics.hxx> +#include <odb/statement.hxx> + #include <libbutl/process.mxx> #include <libbutl/process-io.mxx> // 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<trace_mark&> (*this) << "PREPARE " << s.text (); + } + + void trace_mark_base:: + execute (odb::connection&, const char* stmt) + { + if (verb >= 5) + static_cast<trace_mark&> (*this) << stmt; + } + + void trace_mark_base:: + deallocate (odb::connection&, const odb::statement& s) + { + if (verb >= 6) + static_cast<trace_mark&> (*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 <odb/tracer.hxx> + #include <libbutl/diagnostics.mxx> #include <bdep/types.hxx> // Note: not <bdep/utility.hxx> @@ -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<trace_mark_base>; 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 <bdep/configuration.cli>; +include <bdep/project.cli>; "\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 <bdep/wrapper-traits.hxx>' \ + --hxx-prologue '#include <bdep/wrapper-traits.hxx>' \ + --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 <bdep/common.cli>; 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 + { + "<dir>", + "Specify the build configuration to use as a directory." + } + + bool --all|-a + { + "Use all build configurations." + } + + // Storage for configuration names specified as @<cfg-name>. + // + // Note that we leave it undocumented so that it's not mentioned in + // documentation. + // + strings --config-name; + dir_paths --directory|-d { "<dir>", 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 <odb/core.hxx> + #include <bdep/types.hxx> #include <bdep/utility.hxx> #include <bdep/project-options.hxx> +#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 +// <assert.h> 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 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <model version="1"> + <table name="configuration" kind="object"> + <column name="path" type="TEXT" null="true"/> + <column name="name" type="TEXT" null="true"/> + <primary-key> + <column name="name"/> + </primary-key> + </table> + </model> +</changelog> 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 <odb/version.hxx> + +$libodb.check(LIBODB_VERSION, LIBODB_SNAPSHOT)$ + +#include <odb/sqlite/version.hxx> + +$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 <libbutl/optional.mxx> + +#include <odb/wrapper-traits.hxx> + +namespace odb +{ + template <typename T> + class wrapper_traits<butl::optional<T>> + { + public: + typedef T wrapped_type; + typedef butl::optional<T> wrapper_type; + + // T can be const. + // + typedef + typename odb::details::meta::remove_const<T>::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<unrestricted_wrapped_type&> (*o); + } + }; +} + +#endif // BDEP_WRAPPER_TRAITS_HXX @@ -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) |