aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-01-08 13:27:15 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-01-08 13:27:15 +0200
commitab4421747146aa7995f0cfb1a639c9121c82c915 (patch)
treedeb08893c02ed0238f73becbbe80ede5568b946e
parentd3e624ef7c0fb274e62b81c4c7bd59640770520a (diff)
Implement tracing support
Also use to-relative path translation in diagnostics.
-rw-r--r--build/algorithm.cxx9
-rw-r--r--build/b.cxx27
-rw-r--r--build/buildfile6
-rw-r--r--build/context21
-rw-r--r--build/context.cxx36
-rw-r--r--build/cxx/rule.cxx56
-rw-r--r--build/diagnostics14
-rw-r--r--build/diagnostics.cxx20
-rw-r--r--build/parser.cxx1
-rw-r--r--build/path19
-rw-r--r--build/path.txx30
-rw-r--r--build/pre.build0
-rw-r--r--build/prerequisite.cxx33
-rw-r--r--build/target.cxx16
-rw-r--r--build/trace71
-rw-r--r--build/trace.cxx12
-rw-r--r--tests/build/lexer/buildfile8
-rw-r--r--tests/build/path/driver.cxx193
18 files changed, 544 insertions, 28 deletions
diff --git a/build/algorithm.cxx b/build/algorithm.cxx
index fac0bf6..419bfaf 100644
--- a/build/algorithm.cxx
+++ b/build/algorithm.cxx
@@ -23,6 +23,8 @@ namespace build
target*
search (prerequisite& p)
{
+ tracer tr ("search");
+
assert (p.target == nullptr);
//@@ TODO for now we just default to the directory scope.
@@ -36,7 +38,7 @@ namespace build
d.normalize ();
}
- //@@ TODO would be nice to first check if this target is
+ //@@ TODO: would be nice to first check if this target is
// already in the set before allocating a new instance.
// Find or insert.
@@ -45,8 +47,9 @@ namespace build
targets.emplace (
unique_ptr<target> (p.type.factory (p.name, move (d)))));
- //if (r.second)
- // cout << "new target for prerequsite " << p << " " << d << endl;
+ trace (4, [&]{
+ tr << (r.second ? "new" : "existing") << " target " << **r.first
+ << " for prerequsite " << p;});
return (p.target = r.first->get ());
}
diff --git a/build/b.cxx b/build/b.cxx
index a5123f1..ad68e21 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -110,6 +110,8 @@ namespace build
void
dump ()
{
+ cout << endl;
+
for (const auto& pt: targets)
{
target& t (*pt);
@@ -123,6 +125,8 @@ namespace build
cout << endl;
}
+
+ cout << endl;
}
}
@@ -138,10 +142,16 @@ using namespace build;
int
main (int argc, char* argv[])
{
+ tracer tr ("main");
+
// Initialize time conversion data that is used by localtime_r().
//
tzset ();
+ // Trace verbosity.
+ //
+ verb = 5;
+
// Register target types.
//
target_types.insert (file::static_type);
@@ -200,12 +210,15 @@ main (int argc, char* argv[])
else
out_root = out_base.directory (src_base.leaf (src_root));
- cerr << "work dir: " << work << endl;
- cerr << "home dir: " << home << endl;
- cerr << "out_base: " << out_base << endl;
- cerr << "src_base: " << src_base << endl;
- cerr << "out_root: " << out_root << endl;
- cerr << "src_root: " << src_root << endl;
+ if (verb >= 4)
+ {
+ tr << "work dir: " << work.string ();
+ tr << "home dir: " << home.string ();
+ tr << "out_base: " << out_base.string ();
+ tr << "src_base: " << src_base.string ();
+ tr << "out_root: " << out_root.string ();
+ tr << "src_root: " << src_root.string ();
+ }
// Parse buildfile.
//
@@ -267,7 +280,7 @@ main (int argc, char* argv[])
if (!match_recursive (d))
return 1; // Diagnostics has already been issued.
- //dump ();
+ dump ();
switch (update (d))
{
diff --git a/build/buildfile b/build/buildfile
index 88ea4d6..3afe19b 100644
--- a/build/buildfile
+++ b/build/buildfile
@@ -1,16 +1,18 @@
-exe{b1}: obj{b algorithm scope parser lexer target prerequisite rule \
- native context cxx/target cxx/rule process timestamp path}
+exe{b1}: obj{b algorithm scope parser lexer trace target prerequisite rule \
+ native context diagnostics cxx/target cxx/rule process timestamp path}
obj{b}: cxx{b}
obj{algorithm}: cxx{algorithm}
obj{scope}: cxx{scope}
obj{parser}: cxx{parser}
obj{lexer}: cxx{lexer}
+obj{trace}: cxx{trace}
obj{target}: cxx{target}
obj{prerequisite}: cxx{prerequisite}
obj{rule}: cxx{rule}
obj{native}: cxx{native}
obj{context}: cxx{context}
+obj{diagnostics}: cxx{diagnostics}
obj{cxx/target}: cxx{cxx/target}
obj{cxx/rule}: cxx{cxx/rule}
obj{process}: cxx{process}
diff --git a/build/context b/build/context
index 4c86b14..a369ade 100644
--- a/build/context
+++ b/build/context
@@ -5,6 +5,9 @@
#ifndef BUILD_CONTEXT
#define BUILD_CONTEXT
+#include <string>
+#include <ostream>
+
#include <build/path>
namespace build
@@ -17,6 +20,24 @@ namespace build
extern path src_base;
extern path out_base;
+
+ // If possible, translate an absolute, normalized path into relative to
+ // the work directory.
+ //
+ path
+ translate (const path&);
+
+ // In addition to calling translate() above, this function also uses
+ // shorter notations such as ~/.
+ //
+ std::string
+ diagnostic_string (const path&);
+
+ inline std::ostream&
+ operator<< (std::ostream& os, const path& p)
+ {
+ return os << diagnostic_string (p);
+ }
}
#endif // BUILD_CONTEXT
diff --git a/build/context.cxx b/build/context.cxx
index ce80324..fc4ec1c 100644
--- a/build/context.cxx
+++ b/build/context.cxx
@@ -2,6 +2,8 @@
// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
// license : MIT; see accompanying LICENSE file
+#include <ostream>
+
#include <build/context>
using namespace std;
@@ -16,4 +18,38 @@ namespace build
path src_base;
path out_base;
+
+ path
+ translate (const path& p)
+ {
+ if (p.sub (work))
+ return p.leaf (work);
+
+ // If work is a sub-path of {src,out}_root and this path is also a
+ // sub-bath of it, then use '..' to form a relative path.
+ //
+ if (work.sub (src_root) && p.sub (src_root) ||
+ work.sub (out_root) && p.sub (out_root)) // @@ cache
+ return p.relative (work);
+
+ return p;
+ }
+
+ std::string
+ diagnostic_string (const path& p)
+ {
+ if (p.absolute ())
+ {
+ path rp (translate (p));
+
+#ifndef _WIN32
+ if (rp.absolute () && rp.sub (home))
+ return "~/" + rp.leaf (home).string ();
+#endif
+
+ return rp.string ();
+ }
+
+ return p.string ();
+ }
}
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index 3cab2d4..1d2a2c4 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -17,6 +17,7 @@
#include <build/process>
#include <build/timestamp>
#include <build/diagnostics>
+#include <build/context>
using namespace std;
@@ -29,6 +30,8 @@ namespace build
recipe compile::
match (target& t) const
{
+ tracer tr ("cxx::compile::match");
+
// @@ TODO:
//
// - check prerequisites: single source file
@@ -59,7 +62,7 @@ namespace build
if (sp == nullptr)
{
- cout << "no source file" << endl;
+ trace (3, [&]{tr << "no c++ source file for target " << t;});
return recipe ();
}
@@ -137,16 +140,27 @@ namespace build
void compile::
inject_prerequisites (obj& o, const cxx& s, scope& ds) const
{
+ tracer tr ("cxx::compile::inject_prerequisites");
+
+ // We are using absolute source file path in order to get
+ // absolute paths in the result.
+ //
const char* args[] = {
"g++-4.9",
"-std=c++14",
- "-I..",
- "-MM", //@@ TMP -M
+ "-I", src_root.string ().c_str (),
+ "-MM", //@@ -M
"-MG", // Treat missing headers as generated.
"-MQ", "*", // Quoted target (older version can't handle empty name).
s.path ().string ().c_str (),
nullptr};
+ if (verb >= 2)
+ print_process (args);
+
+ if (verb >= 5)
+ tr << "target: " << o;
+
try
{
process pr (args, false, false, true);
@@ -186,6 +200,9 @@ namespace build
path file (next (l, pos));
file.normalize ();
+ if (verb >= 5)
+ tr << "prerequisite path: " << file.string ();
+
// If there is no extension (e.g., standard C++ headers),
// then assume it is a header. Otherwise, let the standard
// mechanism derive the type from the extension.
@@ -282,17 +299,26 @@ namespace build
if (!u)
return target_state::uptodate;
+ // Translate paths to relative (to working directory) ones. This
+ // results in easier to read diagnostics.
+ //
+ path ro (translate (o.path ()));
+ path rs (translate (s->path ()));
+
const char* args[] = {
"g++-4.9",
"-std=c++14",
"-g",
- "-I..",
+ "-I", src_root.string ().c_str (),
"-c",
- "-o", o.path ().string ().c_str (),
- s->path ().string ().c_str (),
+ "-o", ro.string ().c_str (),
+ rs.string ().c_str (),
nullptr};
- cerr << "c++ " << *s << endl;
+ if (verb >= 1)
+ print_process (args);
+ else
+ cerr << "c++ " << *s << endl;
try
{
@@ -407,19 +433,29 @@ namespace build
if (!u)
return target_state::uptodate;
+ // Translate paths to relative (to working directory) ones. This
+ // results in easier to read diagnostics.
+ //
+ path re (translate (e.path ()));
+ vector<path> ro;
+
vector<const char*> args {"g++-4.9", "-std=c++14", "-g", "-o"};
- args.push_back (e.path ().string ().c_str ());
+ args.push_back (re.string ().c_str ());
for (const prerequisite& p: t.prerequisites)
{
const obj& o (dynamic_cast<const obj&> (*p.target));
- args.push_back (o.path ().string ().c_str ());
+ ro.push_back (translate (o.path ()));
+ args.push_back (ro.back ().string ().c_str ());
}
args.push_back (nullptr);
- cerr << "ld " << e << endl;
+ if (verb >= 1)
+ print_process (args);
+ else
+ cerr << "ld " << e << endl;
try
{
diff --git a/build/diagnostics b/build/diagnostics
index c8fb169..98f481f 100644
--- a/build/diagnostics
+++ b/build/diagnostics
@@ -6,9 +6,12 @@
#define BUILD_DIAGNOSTICS
#include <tuple>
+#include <vector>
#include <utility>
#include <exception>
+#include <build/trace>
+
namespace build
{
// Throw this exception to terminate the build. The handler should
@@ -16,6 +19,17 @@ namespace build
//
class error: public std::exception {};
+ // Print process commmand line.
+ //
+ void
+ print_process (const char* const* args);
+
+ inline void
+ print_process (const std::vector<const char*>& args)
+ {
+ print_process (args.data ());
+ }
+
// Call a function if there is an exception.
//
template <typename F, typename T>
diff --git a/build/diagnostics.cxx b/build/diagnostics.cxx
new file mode 100644
index 0000000..f213707
--- /dev/null
+++ b/build/diagnostics.cxx
@@ -0,0 +1,20 @@
+// file : build/diagnostics.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <build/diagnostics>
+
+#include <iostream>
+
+using namespace std;
+
+namespace build
+{
+ void
+ print_process (const char* const* args)
+ {
+ for (const char* const* p (args); *p != nullptr; p++)
+ cerr << (p != args ? " " : "") << *p;
+ cerr << endl;
+ }
+}
diff --git a/build/parser.cxx b/build/parser.cxx
index e300201..289e571 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -243,6 +243,7 @@ namespace build
if (p.relative ())
p = prev.path () / p;
+ p.normalize ();
scope_ = &scopes[p];
// A directory scope can contain anything that a top level can.
diff --git a/build/path b/build/path
index ccc1b85..92832a1 100644
--- a/build/path
+++ b/build/path
@@ -218,6 +218,16 @@ namespace build
bool
root () const;
+ // Return true if *this is a sub-path of the specified path (i.e.,
+ // the specified path is a prefix). Expects both paths to be
+ // normalized.
+ //
+ bool
+ sub (const basic_path& p) const
+ {
+ return path_.compare (0, p.path_.size (), p.path_) == 0;
+ }
+
public:
// Return the path without the directory part.
//
@@ -249,6 +259,13 @@ namespace build
basic_path
base () const;
+ // Return a path relative to the specified path that is equivalent
+ // to *this. Throws invalid_path if a relative path cannot be derived
+ // (e.g., paths are on different drives on Windows).
+ //
+ basic_path
+ relative (basic_path) const;
+
public:
// Normalize the path. This includes collapsing the '.' and '..'
// directories if possible, collapsing multiple directory
@@ -334,12 +351,14 @@ namespace build
string_type path_;
};
+ /*
template <typename C>
inline std::basic_ostream<C>&
operator<< (std::basic_ostream<C>& os, basic_path<C> const& p)
{
return os << p.string ();
}
+ */
}
#include <build/path.ixx>
diff --git a/build/path.txx b/build/path.txx
index 6a01547..3e952d8 100644
--- a/build/path.txx
+++ b/build/path.txx
@@ -117,8 +117,12 @@ namespace build
if (m < n || path_.compare (0, n, d.path_) != 0)
throw invalid_basic_path<C> (path_);
- if (n != m)
- n++; // Skip the directory separator.
+ if (n != m
+#ifndef _WIN32
+ && !d.root ()
+#endif
+ )
+ n++; // Skip the directory separator (unless it is POSIX root).
return basic_path (path_.c_str () + n, m - n);
}
@@ -144,6 +148,28 @@ namespace build
}
template <typename C>
+ basic_path<C> basic_path<C>::
+ relative (basic_path<C> d) const
+ {
+ basic_path r;
+
+ for (;; d = d.directory ())
+ {
+ if (sub (d))
+ break;
+
+ r /= path ("..");
+
+ // Roots of the paths do not match.
+ //
+ if (d.root ())
+ throw invalid_basic_path<C> (path_);
+ }
+
+ return r / leaf (d);
+ }
+
+ template <typename C>
basic_path<C>& basic_path<C>::
normalize ()
{
diff --git a/build/pre.build b/build/pre.build
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build/pre.build
diff --git a/build/prerequisite.cxx b/build/prerequisite.cxx
index 8153a6c..c43827d 100644
--- a/build/prerequisite.cxx
+++ b/build/prerequisite.cxx
@@ -6,7 +6,9 @@
#include <ostream>
+#include <build/scope>
#include <build/target> // target_type
+#include <build/context>
using namespace std;
@@ -15,9 +17,36 @@ namespace build
ostream&
operator<< (ostream& os, const prerequisite& p)
{
- // @@ TODO: need to come up with a relative (to current) path.
+ if (p.target != nullptr)
+ os << *p.target;
+ else
+ {
+ os << p.type.name << '{';
- return os << p.type.name << '{' << p.name << '}';
+ // Print scope unless the directory is absolute.
+ //
+ if (!p.directory.absolute ())
+ {
+ string s (diagnostic_string (p.scope.path ()));
+
+ if (!s.empty ())
+ os << s << path::traits::directory_separator << ": ";
+ }
+
+ // Print directory.
+ //
+ if (!p.directory.empty ())
+ {
+ string s (diagnostic_string (p.directory));
+
+ if (!s.empty ())
+ os << s << path::traits::directory_separator;
+ }
+
+ os << p.name << '}';
+ }
+
+ return os;
}
bool
diff --git a/build/target.cxx b/build/target.cxx
index 8314ba4..4dffe7f 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -6,6 +6,8 @@
#include <ostream>
+#include <build/context>
+
using namespace std;
namespace build
@@ -15,9 +17,19 @@ namespace build
ostream&
operator<< (ostream& os, const target& t)
{
- // @@ TODO: need to come up with a relative (to current) path.
+ os << t.type ().name << '{';
+
+ if (!t.directory.empty ())
+ {
+ string s (diagnostic_string (t.directory));
+
+ if (!s.empty ())
+ os << s << path::traits::directory_separator;
+ }
+
+ os << t.name << '}';
- return os << t.type ().name << '{' << t.name << '}';
+ return os;
}
target_set targets;
diff --git a/build/trace b/build/trace
new file mode 100644
index 0000000..f1c0567
--- /dev/null
+++ b/build/trace
@@ -0,0 +1,71 @@
+// file : build/trace -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD_TRACE
+#define BUILD_TRACE
+
+#include <cstdint>
+#include <iostream>
+
+namespace build
+{
+ // 1 - command lines to update explicit targets (e.g., .o)
+ // 2 - command lines to update implicit targets (e.g., .d)
+ // 3 - things didn't work out (e.g., rule did not match)
+ // 4 - additional information
+ // 5 - more additional information
+ //
+ extern std::uint8_t verb;
+
+ struct tracer
+ {
+ explicit
+ tracer (const char* name): name_ (name) {}
+
+ struct record
+ {
+ ~record () {if (!empty_) std::cerr << std::endl;}
+
+ template <typename T>
+ std::ostream&
+ operator<< (const T& x) const
+ {
+ return std::cerr << x;
+ }
+
+ // Movable-only type.
+ //
+ explicit record (bool e = true): empty_ (e) {}
+ record (record&& r) {empty_ = r.empty_; r.empty_ = true;}
+ record& operator= (record&& r) {empty_ = r.empty_; r.empty_ = true;}
+
+ record (const record&) = delete;
+ record& operator= (const record&) = delete;
+
+ private:
+ mutable bool empty_;
+ };
+
+ template <typename T>
+ record
+ operator<< (const T& x) const
+ {
+ std::cerr << "trace: " << name_ << ": " << x;
+ return record (false);
+ }
+
+ private:
+ const char* name_;
+ };
+
+ template <typename F>
+ inline void
+ trace (std::uint8_t level, const F& f)
+ {
+ if (verb >= level)
+ f ();
+ }
+}
+
+#endif // BUILD_TRACE
diff --git a/build/trace.cxx b/build/trace.cxx
new file mode 100644
index 0000000..a7e6566
--- /dev/null
+++ b/build/trace.cxx
@@ -0,0 +1,12 @@
+// file : build/trace.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <build/trace>
+
+using namespace std;
+
+namespace build
+{
+ uint8_t verb;
+}
diff --git a/tests/build/lexer/buildfile b/tests/build/lexer/buildfile
new file mode 100644
index 0000000..06e1f75
--- /dev/null
+++ b/tests/build/lexer/buildfile
@@ -0,0 +1,8 @@
+exe{driver}: obj{driver ../../../build/lexer}
+
+obj{driver}: cxx{driver}
+
+../../../build/:
+{
+ obj{lexer}: cxx{lexer}
+}
diff --git a/tests/build/path/driver.cxx b/tests/build/path/driver.cxx
new file mode 100644
index 0000000..739146b
--- /dev/null
+++ b/tests/build/path/driver.cxx
@@ -0,0 +1,193 @@
+// file : tests/build/path/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <cassert>
+#include <iostream>
+
+#include <build/path>
+
+using namespace std;
+using namespace build;
+
+int
+main ()
+{
+ assert (path ("/").string () == "/");
+ assert (path ("//").string () == "/");
+ assert (path ("/tmp/foo/").string () == "/tmp/foo");
+#ifdef _WIN32
+ assert (path ("\\\\").string () == "\\");
+ assert (path ("/\\").string () == "/");
+ assert (path ("C:").string () == "C:");
+ assert (path ("C:\\").string () == "C:");
+ assert (path ("C:\\tmp\\foo\\").string () == "C:\\tmp\\foo");
+#endif
+
+ // abslote/relative/root
+ //
+#ifndef _WIN32
+ assert (path ("/").root ());
+ assert (path ("//").root ());
+ assert (path ("/").absolute ());
+ assert (path ("/foo/bar").absolute ());
+ assert (path ("bar/baz").relative ());
+#else
+ assert (path ("C:").root ());
+ assert (path ("C:\\").root ());
+ assert (path ("C:\\").absolute ());
+ assert (path ("C:\\foo\\bar").absolute ());
+ assert (path ("bar\\baz").relative ());
+#endif
+
+
+ // leaf
+ //
+#ifndef _WIN32
+ assert (path ("/").leaf ().string () == "");
+ assert (path ("/tmp").leaf ().string () == "tmp");
+ assert (path ("//tmp").leaf ().string () == "tmp");
+#else
+ assert (path ("C:").leaf ().string () == "C:");
+ assert (path ("C:\\tmp").leaf ().string () == "tmp");
+ assert (path ("C:\\\\tmp").leaf ().string () == "tmp");
+#endif
+
+ // directory
+ //
+#ifndef _WIN32
+ assert (path ("/").directory ().string () == "");
+ assert (path ("/tmp").directory ().string () == "/");
+ assert (path ("//tmp").directory ().string () == "/");
+#else
+ assert (path ("C:").directory ().string () == "");
+ assert (path ("C:\\tmp").directory ().string () == "C:");
+ assert (path ("C:\\\\tmp").directory ().string () == "C:");
+#endif
+
+ // base
+ //
+ assert (path ("/").base ().string () == "/");
+ assert (path ("/foo.txt").base ().string () == "/foo");
+ assert (path (".txt").base ().string () == ".txt");
+ assert (path ("/.txt").base ().string () == "/.txt");
+ assert (path ("foo.txt.orig").base ().string () == "foo.txt");
+#ifdef _WIN32
+ assert (path ("C:").base ().string () == "C:");
+ assert (path ("C:\\foo.txt").base ().string () == "C:\\foo");
+#endif
+
+ // operator/
+ //
+#ifndef _WIN32
+ assert ((path ("/") / path ("tmp")).string () == "/tmp");
+ assert ((path ("foo") / path ("bar")).string () == "foo/bar");
+#else
+ assert ((path ("\\") / path ("tmp")).string () == "\\tmp");
+ assert ((path ("C:\\") / path ("tmp")).string () == "C:\\tmp");
+ assert ((path ("foo") / path ("bar")).string () == "foo\\bar");
+#endif
+
+ // normalize
+ //
+#ifndef _WIN32
+ assert (path ("../foo").normalize ().string () == "../foo");
+ assert (path ("..///foo").normalize ().string () == "../foo");
+ assert (path ("../../foo").normalize ().string () == "../../foo");
+ assert (path (".././foo").normalize ().string () == "../foo");
+ assert (path (".").normalize ().string () == "");
+ assert (path ("./..").normalize ().string () == "..");
+ assert (path ("../.").normalize ().string () == "..");
+ assert (path ("foo/./..").normalize ().string () == "");
+ assert (path ("/foo/./..").normalize ().string () == "/");
+ assert (path ("./foo").normalize ().string () == "foo");
+#else
+ assert (path ("../foo").normalize ().string () == "..\\foo");
+ assert (path ("..///foo").normalize ().string () == "..\\foo");
+ assert (path ("..\\../foo").normalize ().string () == "..\\..\\foo");
+ assert (path (".././foo").normalize ().string () == "..\\foo");
+ assert (path (".").normalize ().string () == "");
+ assert (path ("./..").normalize ().string () == "..");
+ assert (path ("../.").normalize ().string () == "..");
+ assert (path ("foo/./..").normalize ().string () == "");
+ assert (path ("C:/foo/./..").normalize ().string () == "C:");
+ assert (path ("./foo").normalize ().string () == "foo");
+
+ assert (path ("C:").normalize ().string () == "C:");
+ assert (path ("C:\\Foo12//Bar").normalize ().string () == "C:\\Foo12\\Bar");
+#endif
+
+ // comparison
+ //
+ assert (path ("./foo") == path("./foo"));
+ assert (path ("./boo") < path("./foo"));
+#ifdef _WIN32
+ assert (path (".\\foo") == path("./FoO"));
+ assert (path (".\\boo") < path(".\\Foo"));
+#endif
+
+ // posix_string
+ //
+ assert (path ("foo/bar/../baz").posix_string () == "foo/bar/../baz");
+#ifdef _WIN32
+ assert (path ("foo\\bar\\..\\baz").posix_string () == "foo/bar/../baz");
+ try
+ {
+ path ("c:\\foo\\bar\\..\\baz").posix_string ();
+ assert (false);
+ }
+ catch (const invalid_path&) {}
+#endif
+
+ // sub
+ //
+ assert (path ("foo").sub (path ("foo")));
+ assert (path ("foo/bar").sub (path ("foo/bar")));
+ assert (path ("foo/bar").sub (path ("foo")));
+ assert (!path ("foo/bar").sub (path ("bar")));
+ assert (path ("/foo/bar").sub (path ("/foo")));
+ assert (path ("/foo/bar/baz").sub (path ("/foo/bar")));
+ assert (!path ("/foo/bar/baz").sub (path ("/foo/baz")));
+#ifdef _WIN32
+ assert (path ("c:").sub (path ("c:")));
+ assert (!path ("c:").sub (path ("d:")));
+ assert (path ("c:\\foo").sub (path ("c:")));
+#else
+ assert (path ("/foo/bar/baz").sub (path ("/")));
+#endif
+
+ // relative
+ //
+ assert (path ("foo").relative (path ("foo")) == path ());
+ assert (path ("foo/bar").relative (path ("foo/bar")) == path ());
+ assert (path ("foo/bar/baz").relative (path ("foo/bar")) == path ("baz"));
+ assert (path ("foo/bar/baz").relative (path ("foo/bar/buz")).
+ posix_string () == "../baz");
+ assert (path ("foo/bar/baz").relative (path ("foo/biz/baz")).
+ posix_string () == "../../bar/baz");
+ assert (path ("foo/bar/baz").relative (path ("fox/bar/baz")).
+ posix_string () == "../../../foo/bar/baz");
+#ifdef _WIN32
+ assert (path ("c:\\foo\\bar").relative (path ("c:\\fox\\bar")) ==
+ path ("..\\..\\foo\\bar"));
+ try
+ {
+ path ("c:\\foo\\bar").relative (path ("d:\\fox\\bar"));
+ assert (false);
+ }
+ catch (const invalid_path&) {}
+#else
+ assert (path ("/foo/bar/baz").relative (path ("/")) ==
+ path ("foo/bar/baz"));
+#endif
+
+ /*
+ path p ("../foo");
+ p.complete ();
+
+ cerr << path::current () << endl;
+ cerr << p << endl;
+ p.normalize ();
+ cerr << p << endl;
+ */
+}