]> git.tdb.fi Git - builder.git/commitdiff
Rearrange sources into subdirectories
authorMikko Rasa <tdb@tdb.fi>
Tue, 27 Dec 2022 20:52:34 +0000 (22:52 +0200)
committerMikko Rasa <tdb@tdb.fi>
Wed, 28 Dec 2022 14:11:02 +0000 (16:11 +0200)
364 files changed:
.gitignore
Build
plugins/android/androidapplicationcomponent.cpp [new file with mode: 0644]
plugins/android/androidapplicationcomponent.h [new file with mode: 0644]
plugins/android/androidarchiver.cpp [new file with mode: 0644]
plugins/android/androidarchiver.h [new file with mode: 0644]
plugins/android/androidassetpackagingtool.cpp [new file with mode: 0644]
plugins/android/androidassetpackagingtool.h [new file with mode: 0644]
plugins/android/androidcompiler.cpp [new file with mode: 0644]
plugins/android/androidcompiler.h [new file with mode: 0644]
plugins/android/androidlinker.cpp [new file with mode: 0644]
plugins/android/androidlinker.h [new file with mode: 0644]
plugins/android/androidmanifestfile.cpp [new file with mode: 0644]
plugins/android/androidmanifestfile.h [new file with mode: 0644]
plugins/android/androidmanifestgenerator.cpp [new file with mode: 0644]
plugins/android/androidmanifestgenerator.h [new file with mode: 0644]
plugins/android/androidpackagefile.cpp [new file with mode: 0644]
plugins/android/androidpackagefile.h [new file with mode: 0644]
plugins/android/androidresourcebundle.cpp [new file with mode: 0644]
plugins/android/androidresourcebundle.h [new file with mode: 0644]
plugins/android/androidresourcefile.cpp [new file with mode: 0644]
plugins/android/androidresourcefile.h [new file with mode: 0644]
plugins/android/androidtools.cpp [new file with mode: 0644]
plugins/android/androidtools.h [new file with mode: 0644]
plugins/android/apkbuilder.cpp [new file with mode: 0644]
plugins/android/apkbuilder.h [new file with mode: 0644]
plugins/android/jarsigner.cpp [new file with mode: 0644]
plugins/android/jarsigner.h [new file with mode: 0644]
plugins/builtin/builtintools.cpp [new file with mode: 0644]
plugins/builtin/builtintools.h [new file with mode: 0644]
plugins/builtin/compilecommandsgenerator.cpp [new file with mode: 0644]
plugins/builtin/compilecommandsgenerator.h [new file with mode: 0644]
plugins/builtin/compilecommandsjson.cpp [new file with mode: 0644]
plugins/builtin/compilecommandsjson.h [new file with mode: 0644]
plugins/builtin/copy.cpp [new file with mode: 0644]
plugins/builtin/copy.h [new file with mode: 0644]
plugins/builtin/pkgconfigfile.cpp [new file with mode: 0644]
plugins/builtin/pkgconfigfile.h [new file with mode: 0644]
plugins/builtin/pkgconfiggenerator.cpp [new file with mode: 0644]
plugins/builtin/pkgconfiggenerator.h [new file with mode: 0644]
plugins/builtin/tar.cpp [new file with mode: 0644]
plugins/builtin/tar.h [new file with mode: 0644]
plugins/builtin/tarball.cpp [new file with mode: 0644]
plugins/builtin/tarball.h [new file with mode: 0644]
plugins/builtin/vcxprojectfile.cpp [new file with mode: 0644]
plugins/builtin/vcxprojectfile.h [new file with mode: 0644]
plugins/builtin/vcxprojectgenerator.cpp [new file with mode: 0644]
plugins/builtin/vcxprojectgenerator.h [new file with mode: 0644]
plugins/builtin/vssolutionfile.cpp [new file with mode: 0644]
plugins/builtin/vssolutionfile.h [new file with mode: 0644]
plugins/builtin/vssolutiongenerator.cpp [new file with mode: 0644]
plugins/builtin/vssolutiongenerator.h [new file with mode: 0644]
plugins/clang/clangcompiler.cpp [new file with mode: 0644]
plugins/clang/clangcompiler.h [new file with mode: 0644]
plugins/clang/clanglinker.cpp [new file with mode: 0644]
plugins/clang/clanglinker.h [new file with mode: 0644]
plugins/clang/clangtools.cpp [new file with mode: 0644]
plugins/clang/clangtools.h [new file with mode: 0644]
plugins/datafile/datacollection.cpp [new file with mode: 0644]
plugins/datafile/datacollection.h [new file with mode: 0644]
plugins/datafile/datapack.cpp [new file with mode: 0644]
plugins/datafile/datapack.h [new file with mode: 0644]
plugins/datafile/datapackcomponent.cpp [new file with mode: 0644]
plugins/datafile/datapackcomponent.h [new file with mode: 0644]
plugins/datafile/datasourcefile.h [new file with mode: 0644]
plugins/datafile/datatool.cpp [new file with mode: 0644]
plugins/datafile/datatool.h [new file with mode: 0644]
plugins/datafile/datatransform.cpp [new file with mode: 0644]
plugins/datafile/datatransform.h [new file with mode: 0644]
plugins/gnu/gnuarchiver.cpp [new file with mode: 0644]
plugins/gnu/gnuarchiver.h [new file with mode: 0644]
plugins/gnu/gnucompiler.cpp [new file with mode: 0644]
plugins/gnu/gnucompiler.h [new file with mode: 0644]
plugins/gnu/gnulinker.cpp [new file with mode: 0644]
plugins/gnu/gnulinker.h [new file with mode: 0644]
plugins/gnu/gnutools.cpp [new file with mode: 0644]
plugins/gnu/gnutools.h [new file with mode: 0644]
plugins/gnu/mingwdlltool.cpp [new file with mode: 0644]
plugins/gnu/mingwdlltool.h [new file with mode: 0644]
plugins/msvc/microsofttools.cpp [new file with mode: 0644]
plugins/msvc/microsofttools.h [new file with mode: 0644]
plugins/msvc/msvcarchiver.cpp [new file with mode: 0644]
plugins/msvc/msvcarchiver.h [new file with mode: 0644]
plugins/msvc/msvccompiler.cpp [new file with mode: 0644]
plugins/msvc/msvccompiler.h [new file with mode: 0644]
plugins/msvc/msvclinker.cpp [new file with mode: 0644]
plugins/msvc/msvclinker.h [new file with mode: 0644]
source/analyzer.cpp [deleted file]
source/analyzer.h [deleted file]
source/androidapplicationcomponent.cpp [deleted file]
source/androidapplicationcomponent.h [deleted file]
source/androidarchiver.cpp [deleted file]
source/androidarchiver.h [deleted file]
source/androidassetpackagingtool.cpp [deleted file]
source/androidassetpackagingtool.h [deleted file]
source/androidcompiler.cpp [deleted file]
source/androidcompiler.h [deleted file]
source/androidlinker.cpp [deleted file]
source/androidlinker.h [deleted file]
source/androidmanifestfile.cpp [deleted file]
source/androidmanifestfile.h [deleted file]
source/androidmanifestgenerator.cpp [deleted file]
source/androidmanifestgenerator.h [deleted file]
source/androidpackagefile.cpp [deleted file]
source/androidpackagefile.h [deleted file]
source/androidresourcebundle.cpp [deleted file]
source/androidresourcebundle.h [deleted file]
source/androidresourcefile.cpp [deleted file]
source/androidresourcefile.h [deleted file]
source/androidtools.cpp [deleted file]
source/androidtools.h [deleted file]
source/apkbuilder.cpp [deleted file]
source/apkbuilder.h [deleted file]
source/architecture.cpp [deleted file]
source/architecture.h [deleted file]
source/binary.cpp [deleted file]
source/binary.h [deleted file]
source/binarycomponent.cpp [deleted file]
source/binarycomponent.h [deleted file]
source/binarypackage.cpp [deleted file]
source/binarypackage.h [deleted file]
source/booleanevaluator.cpp [deleted file]
source/booleanevaluator.h [deleted file]
source/builder.cpp [deleted file]
source/builder.h [deleted file]
source/buildercli.cpp [deleted file]
source/buildercli.h [deleted file]
source/buildgraph.cpp [deleted file]
source/buildgraph.h [deleted file]
source/buildinfo.cpp [deleted file]
source/buildinfo.h [deleted file]
source/buildtype.cpp [deleted file]
source/buildtype.h [deleted file]
source/builtintools.cpp [deleted file]
source/builtintools.h [deleted file]
source/cache.cpp [deleted file]
source/cache.h [deleted file]
source/chainedtask.cpp [deleted file]
source/chainedtask.h [deleted file]
source/clangcompiler.cpp [deleted file]
source/clangcompiler.h [deleted file]
source/clanglinker.cpp [deleted file]
source/clanglinker.h [deleted file]
source/clangtools.cpp [deleted file]
source/clangtools.h [deleted file]
source/cli/analyzer.cpp [new file with mode: 0644]
source/cli/analyzer.h [new file with mode: 0644]
source/cli/buildercli.cpp [new file with mode: 0644]
source/cli/buildercli.h [new file with mode: 0644]
source/compilecommandsgenerator.cpp [deleted file]
source/compilecommandsgenerator.h [deleted file]
source/compilecommandsjson.cpp [deleted file]
source/compilecommandsjson.h [deleted file]
source/component.cpp [deleted file]
source/component.h [deleted file]
source/conditionalloader.cpp [deleted file]
source/conditionalloader.h [deleted file]
source/config.cpp [deleted file]
source/config.h [deleted file]
source/copy.cpp [deleted file]
source/copy.h [deleted file]
source/csourcefile.cpp [deleted file]
source/csourcefile.h [deleted file]
source/customizedtool.cpp [deleted file]
source/customizedtool.h [deleted file]
source/datacollection.cpp [deleted file]
source/datacollection.h [deleted file]
source/datapack.cpp [deleted file]
source/datapack.h [deleted file]
source/datapackcomponent.cpp [deleted file]
source/datapackcomponent.h [deleted file]
source/datasourcefile.h [deleted file]
source/datatool.cpp [deleted file]
source/datatool.h [deleted file]
source/datatransform.cpp [deleted file]
source/datatransform.h [deleted file]
source/executable.cpp [deleted file]
source/executable.h [deleted file]
source/exportdefinitions.cpp [deleted file]
source/exportdefinitions.h [deleted file]
source/externaltask.cpp [deleted file]
source/externaltask.h [deleted file]
source/feature.cpp [deleted file]
source/feature.h [deleted file]
source/file.h [deleted file]
source/filetarget.cpp [deleted file]
source/filetarget.h [deleted file]
source/gnuarchiver.cpp [deleted file]
source/gnuarchiver.h [deleted file]
source/gnucompiler.cpp [deleted file]
source/gnucompiler.h [deleted file]
source/gnulinker.cpp [deleted file]
source/gnulinker.h [deleted file]
source/gnutools.cpp [deleted file]
source/gnutools.h [deleted file]
source/importlibrary.cpp [deleted file]
source/importlibrary.h [deleted file]
source/installcomponent.cpp [deleted file]
source/installcomponent.h [deleted file]
source/installedfile.cpp [deleted file]
source/installedfile.h [deleted file]
source/installmap.cpp [deleted file]
source/installmap.h [deleted file]
source/internaltask.cpp [deleted file]
source/internaltask.h [deleted file]
source/jarsigner.cpp [deleted file]
source/jarsigner.h [deleted file]
source/lib/architecture.cpp [new file with mode: 0644]
source/lib/architecture.h [new file with mode: 0644]
source/lib/binary.cpp [new file with mode: 0644]
source/lib/binary.h [new file with mode: 0644]
source/lib/binarycomponent.cpp [new file with mode: 0644]
source/lib/binarycomponent.h [new file with mode: 0644]
source/lib/binarypackage.cpp [new file with mode: 0644]
source/lib/binarypackage.h [new file with mode: 0644]
source/lib/booleanevaluator.cpp [new file with mode: 0644]
source/lib/booleanevaluator.h [new file with mode: 0644]
source/lib/builder.cpp [new file with mode: 0644]
source/lib/builder.h [new file with mode: 0644]
source/lib/buildgraph.cpp [new file with mode: 0644]
source/lib/buildgraph.h [new file with mode: 0644]
source/lib/buildinfo.cpp [new file with mode: 0644]
source/lib/buildinfo.h [new file with mode: 0644]
source/lib/buildtype.cpp [new file with mode: 0644]
source/lib/buildtype.h [new file with mode: 0644]
source/lib/cache.cpp [new file with mode: 0644]
source/lib/cache.h [new file with mode: 0644]
source/lib/chainedtask.cpp [new file with mode: 0644]
source/lib/chainedtask.h [new file with mode: 0644]
source/lib/component.cpp [new file with mode: 0644]
source/lib/component.h [new file with mode: 0644]
source/lib/conditionalloader.cpp [new file with mode: 0644]
source/lib/conditionalloader.h [new file with mode: 0644]
source/lib/config.cpp [new file with mode: 0644]
source/lib/config.h [new file with mode: 0644]
source/lib/csourcefile.cpp [new file with mode: 0644]
source/lib/csourcefile.h [new file with mode: 0644]
source/lib/customizedtool.cpp [new file with mode: 0644]
source/lib/customizedtool.h [new file with mode: 0644]
source/lib/executable.cpp [new file with mode: 0644]
source/lib/executable.h [new file with mode: 0644]
source/lib/exportdefinitions.cpp [new file with mode: 0644]
source/lib/exportdefinitions.h [new file with mode: 0644]
source/lib/externaltask.cpp [new file with mode: 0644]
source/lib/externaltask.h [new file with mode: 0644]
source/lib/feature.cpp [new file with mode: 0644]
source/lib/feature.h [new file with mode: 0644]
source/lib/file.h [new file with mode: 0644]
source/lib/filetarget.cpp [new file with mode: 0644]
source/lib/filetarget.h [new file with mode: 0644]
source/lib/importlibrary.cpp [new file with mode: 0644]
source/lib/importlibrary.h [new file with mode: 0644]
source/lib/installcomponent.cpp [new file with mode: 0644]
source/lib/installcomponent.h [new file with mode: 0644]
source/lib/installedfile.cpp [new file with mode: 0644]
source/lib/installedfile.h [new file with mode: 0644]
source/lib/installmap.cpp [new file with mode: 0644]
source/lib/installmap.h [new file with mode: 0644]
source/lib/internaltask.cpp [new file with mode: 0644]
source/lib/internaltask.h [new file with mode: 0644]
source/lib/logger.cpp [new file with mode: 0644]
source/lib/logger.h [new file with mode: 0644]
source/lib/objcsourcefile.cpp [new file with mode: 0644]
source/lib/objcsourcefile.h [new file with mode: 0644]
source/lib/objectfile.cpp [new file with mode: 0644]
source/lib/objectfile.h [new file with mode: 0644]
source/lib/package.cpp [new file with mode: 0644]
source/lib/package.h [new file with mode: 0644]
source/lib/packagemanager.cpp [new file with mode: 0644]
source/lib/packagemanager.h [new file with mode: 0644]
source/lib/pattern.cpp [new file with mode: 0644]
source/lib/pattern.h [new file with mode: 0644]
source/lib/sharedlibrary.cpp [new file with mode: 0644]
source/lib/sharedlibrary.h [new file with mode: 0644]
source/lib/sourcearchivecomponent.cpp [new file with mode: 0644]
source/lib/sourcearchivecomponent.h [new file with mode: 0644]
source/lib/sourcefile.cpp [new file with mode: 0644]
source/lib/sourcefile.h [new file with mode: 0644]
source/lib/sourcegenerator.cpp [new file with mode: 0644]
source/lib/sourcegenerator.h [new file with mode: 0644]
source/lib/sourcepackage.cpp [new file with mode: 0644]
source/lib/sourcepackage.h [new file with mode: 0644]
source/lib/staticlibrary.cpp [new file with mode: 0644]
source/lib/staticlibrary.h [new file with mode: 0644]
source/lib/sysutils.cpp [new file with mode: 0644]
source/lib/sysutils.h [new file with mode: 0644]
source/lib/target.cpp [new file with mode: 0644]
source/lib/target.h [new file with mode: 0644]
source/lib/task.cpp [new file with mode: 0644]
source/lib/task.h [new file with mode: 0644]
source/lib/templatefile.h [new file with mode: 0644]
source/lib/tool.cpp [new file with mode: 0644]
source/lib/tool.h [new file with mode: 0644]
source/lib/toolchain.cpp [new file with mode: 0644]
source/lib/toolchain.h [new file with mode: 0644]
source/lib/virtualfilesystem.cpp [new file with mode: 0644]
source/lib/virtualfilesystem.h [new file with mode: 0644]
source/lib/virtualtarget.cpp [new file with mode: 0644]
source/lib/virtualtarget.h [new file with mode: 0644]
source/logger.cpp [deleted file]
source/logger.h [deleted file]
source/microsofttools.cpp [deleted file]
source/microsofttools.h [deleted file]
source/mingwdlltool.cpp [deleted file]
source/mingwdlltool.h [deleted file]
source/msvcarchiver.cpp [deleted file]
source/msvcarchiver.h [deleted file]
source/msvccompiler.cpp [deleted file]
source/msvccompiler.h [deleted file]
source/msvclinker.cpp [deleted file]
source/msvclinker.h [deleted file]
source/objcsourcefile.cpp [deleted file]
source/objcsourcefile.h [deleted file]
source/objectfile.cpp [deleted file]
source/objectfile.h [deleted file]
source/package.cpp [deleted file]
source/package.h [deleted file]
source/packagemanager.cpp [deleted file]
source/packagemanager.h [deleted file]
source/pattern.cpp [deleted file]
source/pattern.h [deleted file]
source/pkgconfigfile.cpp [deleted file]
source/pkgconfigfile.h [deleted file]
source/pkgconfiggenerator.cpp [deleted file]
source/pkgconfiggenerator.h [deleted file]
source/sharedlibrary.cpp [deleted file]
source/sharedlibrary.h [deleted file]
source/sourcearchivecomponent.cpp [deleted file]
source/sourcearchivecomponent.h [deleted file]
source/sourcefile.cpp [deleted file]
source/sourcefile.h [deleted file]
source/sourcegenerator.cpp [deleted file]
source/sourcegenerator.h [deleted file]
source/sourcepackage.cpp [deleted file]
source/sourcepackage.h [deleted file]
source/staticlibrary.cpp [deleted file]
source/staticlibrary.h [deleted file]
source/sysutils.cpp [deleted file]
source/sysutils.h [deleted file]
source/tar.cpp [deleted file]
source/tar.h [deleted file]
source/tarball.cpp [deleted file]
source/tarball.h [deleted file]
source/target.cpp [deleted file]
source/target.h [deleted file]
source/task.cpp [deleted file]
source/task.h [deleted file]
source/templatefile.h [deleted file]
source/tool.cpp [deleted file]
source/tool.h [deleted file]
source/toolchain.cpp [deleted file]
source/toolchain.h [deleted file]
source/vcxprojectfile.cpp [deleted file]
source/vcxprojectfile.h [deleted file]
source/vcxprojectgenerator.cpp [deleted file]
source/vcxprojectgenerator.h [deleted file]
source/virtualfilesystem.cpp [deleted file]
source/virtualfilesystem.h [deleted file]
source/virtualtarget.cpp [deleted file]
source/virtualtarget.h [deleted file]
source/vssolutionfile.cpp [deleted file]
source/vssolutionfile.h [deleted file]
source/vssolutiongenerator.cpp [deleted file]
source/vssolutiongenerator.h [deleted file]

index f2be0c36c4b6b5d3135bb9531dd103d0d9a8b0e8..e45ec7e2d8dc9618406cae2ebe343a93bd197bb8 100644 (file)
@@ -1,4 +1,13 @@
 /.config
 /builder
+/builder.pc
 /builder-stage1
+/libbuilder.a
+/libbuilder.so
+/libandroidtools.a
+/libbuiltintools.a
+/libclangtools.a
+/libdatatools.a
+/libgnutools.a
+/libmsvctools.a
 /temp
diff --git a/Build b/Build
index 28ab0e2d40263be12a2f3a4719006010535a6f2a..74490dd60d5d643c77129402bcd92124826c613a 100644 (file)
--- a/Build
+++ b/Build
@@ -13,9 +13,55 @@ package "builder"
                standard CXX "c++11";
        };
 
-       program "builder"
+       library "builtintools"
+       {
+               source "plugins/builtin";
+               default false;
+       };
+
+       library "gnutools"
+       {
+               source "plugins/gnu";
+               default false;
+       };
+
+       library "clangtools"
+       {
+               source "plugins/clang";
+               default false;
+       };
+
+       library "msvctools"
+       {
+               source "plugins/msvc";
+               default false;
+       };
+
+       library "androidtools"
+       {
+               source "plugins/android";
+               default false;
+       };
+
+       library "datatools"
        {
-               source "source";
+               source "plugins/datafile";
+               default false;
+       };
+
+       library "builder"
+       {
+               source "source/lib";
+               use "builtintools";
+               use "gnutools";
+               use "clangtools";
+               use "msvctools";
+               use "androidtools";
+               use "datatools";
+               build_info
+               {
+                       incpath "plugins";
+               };
                if_arch "windows"
                {
                        build_info
@@ -25,6 +71,17 @@ package "builder"
                        };
                };
                install true;
+               install_map
+               {
+                       map "source/lib" "include/msp/builder";
+               };
+       };
+
+       program "builder"
+       {
+               source "source/cli";
+               use "builder";
+               install true;
        };
 
        source_archive
diff --git a/plugins/android/androidapplicationcomponent.cpp b/plugins/android/androidapplicationcomponent.cpp
new file mode 100644 (file)
index 0000000..be7e31c
--- /dev/null
@@ -0,0 +1,93 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/installedfile.h>
+#include <msp/builder/sharedlibrary.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/builder/tool.h>
+#include <msp/builder/toolchain.h>
+#include <msp/core/algorithm.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include "androidapplicationcomponent.h"
+#include "androidmanifestfile.h"
+#include "androidresourcefile.h"
+
+using namespace std;
+using namespace Msp;
+
+void AndroidApplicationComponent::create_targets() const
+{
+       Builder &builder = package.get_builder();
+       BuildGraph &build_graph = builder.get_build_graph();
+
+       vector<Target *> contents;
+       for(const auto &kvp: build_graph.get_targets())
+               if(kvp.second->get_package()==&package)
+                       if(InstalledFile *inst = dynamic_cast<InstalledFile *>(kvp.second))
+                               contents.push_back(inst->get_real_target());
+
+       AndroidManifestFile *manifest = new AndroidManifestFile(builder, *this);
+       manifest->set_orientation(orientation);
+       for(const string &p: permissions)
+               manifest->add_permission(p);
+
+       vector<Target *> resource_sources;
+       resource_sources.push_back(manifest);
+
+       const Toolchain &toolchain = builder.get_toolchain();
+       Tool &copy = toolchain.get_tool("CP");
+       for(const FS::Path &s: collect_source_files())
+       {
+               Target *tgt = new AndroidResourceFile(builder, *this, s);
+               resource_sources.push_back(copy.create_target(*tgt, "//"));
+       }
+
+       Tool &aapt = toolchain.get_tool("AAPT");
+       Target *resource_bundle = aapt.create_target(resource_sources);
+
+       vector<Target *> apk_sources;
+       apk_sources.reserve(1+contents.size());
+       apk_sources.push_back(resource_bundle);
+
+       const Architecture &arch = package.get_builder().get_current_arch();
+       string lib_dir = format("//%s/lib/", name);
+       if(arch.get_type()=="arm")
+       {
+               lib_dir += "armeabi";
+               if(arch.get_cpu()=="armv7a")
+                       lib_dir += "-v7a";
+       }
+       else
+               lib_dir += arch.get_type();
+
+       string assets_dir = format("//%s/assets", name);
+       for(Target *t: contents)
+       {
+               Target *staged = 0;
+               if(SharedLibrary *shlib = dynamic_cast<SharedLibrary *>(t))
+               {
+                       manifest->set_native_library(shlib);
+                       staged = copy.create_target(*t, lib_dir);
+               }
+               else
+                       staged = copy.create_target(*t, assets_dir);
+               apk_sources.push_back(staged);
+       }
+
+       Tool &apk_builder = toolchain.get_tool("APK");
+       Target *apk = apk_builder.create_target(apk_sources);
+       builder.get_build_graph().add_primary_target(*apk);
+}
+
+
+AndroidApplicationComponent::Loader::Loader(AndroidApplicationComponent &c):
+       DataFile::DerivedObjectLoader<AndroidApplicationComponent, Component::Loader>(c)
+{
+       add("orientation", &AndroidApplicationComponent::orientation);
+       add("permission", &Loader::permission);
+}
+
+void AndroidApplicationComponent::Loader::permission(const string &perm)
+{
+       if(!any_equals(obj.permissions, perm))
+               obj.permissions.push_back(perm);
+}
diff --git a/plugins/android/androidapplicationcomponent.h b/plugins/android/androidapplicationcomponent.h
new file mode 100644 (file)
index 0000000..e1b30e2
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef ANDROIDAPPLICATIONCOMPONENT_H_
+#define ANDROIDAPPLICATIONCOMPONENT_H_
+
+#include <msp/builder/component.h>
+
+class AndroidApplicationComponent: public Component
+{
+public:
+       class Loader: public Msp::DataFile::DerivedObjectLoader<AndroidApplicationComponent, Component::Loader>
+       {
+       public:
+               Loader(AndroidApplicationComponent &);
+
+       private:
+               void permission(const std::string &);
+       };
+
+private:
+       std::string orientation;
+       std::vector<std::string> permissions;
+
+public:
+       AndroidApplicationComponent(SourcePackage &p, const std::string &n): Component(p, n) { }
+
+       void create_targets() const override;
+};
+
+#endif
diff --git a/plugins/android/androidarchiver.cpp b/plugins/android/androidarchiver.cpp
new file mode 100644 (file)
index 0000000..617445c
--- /dev/null
@@ -0,0 +1,14 @@
+#include "androidarchiver.h"
+#include "androidtools.h"
+
+AndroidArchiver::AndroidArchiver(Builder &b, const Architecture &a, const AndroidNdk &ndk):
+       CustomizedTool(b, "AR", a)
+{
+       set_command("ar", true);
+       if(ndk.get_root_dir().empty())
+               problems.push_back("Android NDK not found");
+       else if(ndk.get_bin_dir().empty())
+               problems.push_back("Android NDK toolchain not found");
+       else
+               set_command((ndk.get_bin_dir()/command).str());
+}
diff --git a/plugins/android/androidarchiver.h b/plugins/android/androidarchiver.h
new file mode 100644 (file)
index 0000000..032f832
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef ANDROIDARCHIVER_H_
+#define ANDROIDARCHIVER_H_
+
+#include <msp/builder/customizedtool.h>
+
+class AndroidNdk;
+
+class AndroidArchiver: public CustomizedTool
+{
+public:
+       AndroidArchiver(Builder &, const Architecture &, const AndroidNdk &);
+};
+
+#endif
diff --git a/plugins/android/androidassetpackagingtool.cpp b/plugins/android/androidassetpackagingtool.cpp
new file mode 100644 (file)
index 0000000..ba321cb
--- /dev/null
@@ -0,0 +1,93 @@
+#include <msp/builder/component.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/core/algorithm.h>
+#include <msp/fs/utils.h>
+#include "androidassetpackagingtool.h"
+#include "androidmanifestfile.h"
+#include "androidresourcebundle.h"
+#include "androidtools.h"
+
+using namespace std;
+using namespace Msp;
+
+AndroidAssetPackagingTool::AndroidAssetPackagingTool(Builder &b, const AndroidSdk &s):
+       Tool(b, "AAPT"),
+       sdk(s)
+{
+       if(sdk.get_root_dir().empty())
+               problems.push_back("Android SDK not found");
+       else if(sdk.get_build_tools_dir().empty())
+               problems.push_back("Android build-tools not found");
+       else
+               set_command((sdk.get_build_tools_dir()/"aapt").str());
+
+       if(sdk.get_platform_jar().empty())
+               problems.push_back("Android platform not found");
+
+       set_run_external(_run);
+}
+
+Target *AndroidAssetPackagingTool::create_target(const vector<Target *> &sources, const string &)
+{
+       AndroidManifestFile *manifest = 0;
+       vector<FileTarget *> resources;
+       resources.reserve(sources.size());
+       for(Target *s: sources)
+       {
+               if(AndroidManifestFile *m = dynamic_cast<AndroidManifestFile *>(s))
+                       manifest = m;
+               else if(FileTarget *f = dynamic_cast<FileTarget *>(s))
+                       resources.push_back(f);
+       }
+
+       if(!manifest)
+               throw invalid_argument("AndroidAssetPackagingTool::create_target");
+
+       AndroidResourceBundle *res = new AndroidResourceBundle(builder, *manifest->get_component(), *manifest, resources);
+       res->set_tool(*this);
+       return res;
+}
+
+ExternalTask::Arguments AndroidAssetPackagingTool::_run(const AndroidResourceBundle &res, FS::Path &work_dir)
+{
+       const AndroidAssetPackagingTool &tool = dynamic_cast<const AndroidAssetPackagingTool &>(*res.get_tool());
+
+       ExternalTask::Arguments argv;
+       argv.push_back(tool.get_executable()->get_path().str());
+       argv.push_back("package");
+
+       argv.push_back("-I");
+       argv.push_back(tool.sdk.get_platform_jar().str());
+
+       argv.push_back("-F");
+       argv.push_back(FS::relative(res.get_path(), work_dir).str());
+
+       vector<FS::Path> resource_dirs;
+       resource_dirs.reserve(res.get_dependencies().size());
+       for(Target *d: res.get_dependencies())
+       {
+               FileTarget *file = dynamic_cast<FileTarget *>(d);
+               Target *real = d->get_real_target();
+
+               if(dynamic_cast<AndroidManifestFile *>(real))
+               {
+                       argv.push_back("-M");
+                       argv.push_back(FS::relative(file->get_path(), work_dir).str());
+               }
+               else if(real->get_package()==res.get_package())
+               {
+                       const FS::Path &path = file->get_path();
+                       FS::Path res_dir = path.subpath(0, path.size()-2);
+                       if(!any_equals(resource_dirs, res_dir))
+                               resource_dirs.push_back(res_dir);
+               }
+       }
+
+       for(const FS::Path &d: resource_dirs)
+       {
+               argv.push_back("-S");
+               argv.push_back(FS::relative(d, work_dir).str());
+       }
+
+       return argv;
+}
diff --git a/plugins/android/androidassetpackagingtool.h b/plugins/android/androidassetpackagingtool.h
new file mode 100644 (file)
index 0000000..c03c097
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef ANDROIDASSETPACKAGINGTOOL_H_
+#define ANDROIDASSETPACKAGINGTOOL_H_
+
+#include <msp/builder/tool.h>
+
+class AndroidResourceBundle;
+class AndroidSdk;
+
+class AndroidAssetPackagingTool: public Tool
+{
+private:
+       const AndroidSdk &sdk;
+
+public:
+       AndroidAssetPackagingTool(Builder &, const AndroidSdk &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static ExternalTask::Arguments _run(const AndroidResourceBundle &, Msp::FS::Path &);
+};
+
+#endif
diff --git a/plugins/android/androidcompiler.cpp b/plugins/android/androidcompiler.cpp
new file mode 100644 (file)
index 0000000..b9d7add
--- /dev/null
@@ -0,0 +1,84 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/externaltask.h>
+#include <msp/builder/filetarget.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include "androidcompiler.h"
+#include "androidtools.h"
+
+using namespace std;
+using namespace Msp;
+
+AndroidCompiler::AndroidCompiler(Builder &b, const Architecture &a, const string &t, const AndroidNdk &n):
+       CustomizedTool(b, t, a),
+       ndk(n)
+{
+       set_command((tag=="CXX" ? "g++" : "gcc"), true);
+       if(ndk.get_root_dir().empty())
+               problems.push_back("Android NDK not found");
+       else if(ndk.get_bin_dir().empty())
+               problems.push_back("Android NDK toolchain not found");
+       else
+               set_command((ndk.get_bin_dir()/command).str());
+
+       if(ndk.get_platform_sysroot().empty())
+               problems.push_back("Android platform not found");
+       else if(!ndk.get_common_sysroot().empty())
+       {
+               build_info.sysroot = ndk.get_common_sysroot();
+               /* The common sysroot has asm headers in arch directories and the
+               compiler doesn't pick them up automatically */
+               build_info.incpath.push_back(ndk.get_common_sysroot()/"usr/include"/architecture->get_cross_prefix());
+       }
+       else
+               build_info.sysroot = ndk.get_platform_sysroot();
+}
+
+void AndroidCompiler::do_prepare(ToolData &tool) const
+{
+       const Architecture &arch = *static_cast<const Tool &>(tool).get_architecture();
+
+       CustomizedTool::do_prepare(tool);
+       if(tag=="CXX")
+       {
+               tool.build_info.libs.push_back("gnustl_static");
+
+               unsigned version = tool.extra_data;
+               string version_str = format("%d.%d.%d", version>>16, (version>>8)&0xFF, version&0xFF);
+               FS::Path libstdcxx_dir = ndk.get_root_dir()/"sources"/"cxx-stl"/"gnu-libstdc++";
+               FS::Path libstdcxx_path;
+               while(1)
+               {
+                       libstdcxx_path = libstdcxx_dir/version_str;
+                       if(FS::exists(libstdcxx_path))
+                               break;
+
+                       string::size_type dot = version_str.rfind('.');
+                       if(dot==string::npos)
+                       {
+                               tool.problems.push_back("C++ standard library not found");
+                               return;
+                       }
+
+                       version_str = version_str.substr(0, dot);
+               }
+
+               builder.get_logger().log("tools", "Found GNU libstdc++ in %s", libstdcxx_path);
+
+               FS::Path public_dir = libstdcxx_path/"include";
+               tool.system_path.push_back(public_dir);
+               tool.build_info.incpath.push_back(public_dir);
+
+               FS::Path arch_path = libstdcxx_path/"libs";
+               builder.get_logger().log("files", "Traversing %s", arch_path.str());
+               string arch_dir = arch.best_match(list_files(arch_path));
+               if(!arch_dir.empty())
+               {
+                       tool.build_info.incpath.push_back(libstdcxx_path/"libs"/arch_dir/"include");
+                       tool.build_info.libpath.push_back(libstdcxx_path/"libs"/arch_dir);
+               }
+       }
+}
diff --git a/plugins/android/androidcompiler.h b/plugins/android/androidcompiler.h
new file mode 100644 (file)
index 0000000..7bfbe11
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef ANDROIDCOMPILER_H_
+#define ANDROIDCOMPILER_H_
+
+#include <msp/builder/customizedtool.h>
+
+class AndroidNdk;
+
+class AndroidCompiler: public CustomizedTool
+{
+private:
+       const AndroidNdk &ndk;
+
+public:
+       AndroidCompiler(Builder &, const Architecture &, const std::string &, const AndroidNdk &);
+
+protected:
+       void do_prepare(ToolData &) const override;
+};
+
+#endif
diff --git a/plugins/android/androidlinker.cpp b/plugins/android/androidlinker.cpp
new file mode 100644 (file)
index 0000000..ec90249
--- /dev/null
@@ -0,0 +1,18 @@
+#include "androidlinker.h"
+#include "androidtools.h"
+
+using namespace std;
+
+AndroidLinker::AndroidLinker(Builder &b, const Architecture &a, const AndroidNdk &ndk):
+       CustomizedTool(b, "LINK", a)
+{
+       build_info.sysroot = ndk.get_platform_sysroot();
+
+       set_command("gcc", true);
+       set_command((ndk.get_bin_dir()/command).str());
+}
+
+Target *AndroidLinker::create_target(const vector<Target *> &sources, const string &)
+{
+       return CustomizedTool::create_target(sources, "shared");
+}
diff --git a/plugins/android/androidlinker.h b/plugins/android/androidlinker.h
new file mode 100644 (file)
index 0000000..71d6bc4
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef ANDROIDLINKER_H_
+#define ANDROIDLINKER_H_
+
+#include <msp/builder/customizedtool.h>
+
+class AndroidNdk;
+
+class AndroidLinker: public CustomizedTool
+{
+public:
+       AndroidLinker(Builder &, const Architecture &, const AndroidNdk &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+};
+
+#endif
diff --git a/plugins/android/androidmanifestfile.cpp b/plugins/android/androidmanifestfile.cpp
new file mode 100644 (file)
index 0000000..ce41769
--- /dev/null
@@ -0,0 +1,33 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/core/algorithm.h>
+#include "androidapplicationcomponent.h"
+#include "androidmanifestfile.h"
+
+using namespace std;
+using namespace Msp;
+
+AndroidManifestFile::AndroidManifestFile(Builder &b, const AndroidApplicationComponent &a):
+       FileTarget(b, a.get_package(), a.get_package().get_temp_directory()/a.get_name()/"AndroidManifest.xml")
+{
+       component = &a;
+       tool = &builder.get_toolchain().get_tool("AMG");
+
+       add_dependency(package->get_build_file());
+}
+
+void AndroidManifestFile::set_native_library(SharedLibrary *lib)
+{
+       native_lib = lib;
+}
+
+void AndroidManifestFile::set_orientation(const string &ori)
+{
+       orientation = ori;
+}
+
+void AndroidManifestFile::add_permission(const string &perm)
+{
+       if(!any_equals(permissions, perm))
+               permissions.push_back(perm);
+}
diff --git a/plugins/android/androidmanifestfile.h b/plugins/android/androidmanifestfile.h
new file mode 100644 (file)
index 0000000..ad6a2da
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef ANDROIDMANIFESTFILE_H_
+#define ANDROIDMANIFESTFILE_H_
+
+#include <vector>
+#include <msp/builder/filetarget.h>
+
+class AndroidApplicationComponent;
+class SharedLibrary;
+
+/**
+Metadata file for an Android application.
+*/
+class AndroidManifestFile: public FileTarget
+{
+private:
+       SharedLibrary *native_lib = 0;
+       std::vector<std::string> permissions;
+       std::string orientation;
+
+public:
+       AndroidManifestFile(Builder &, const AndroidApplicationComponent &);
+
+       const char *get_type() const override { return "AndroidManifestFile"; }
+
+       void set_native_library(SharedLibrary *);
+       SharedLibrary *get_native_library() const { return native_lib; }
+
+       void add_permission(const std::string &);
+       void set_orientation(const std::string &);
+       const std::vector<std::string> &get_permissions() const { return permissions; }
+       const std::string &get_orientation() const { return orientation; }
+};
+
+#endif
diff --git a/plugins/android/androidmanifestgenerator.cpp b/plugins/android/androidmanifestgenerator.cpp
new file mode 100644 (file)
index 0000000..cf03bee
--- /dev/null
@@ -0,0 +1,58 @@
+#include <msp/builder/component.h>
+#include <msp/builder/sharedlibrary.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include "androidmanifestfile.h"
+#include "androidmanifestgenerator.h"
+
+using namespace std;
+using namespace Msp;
+
+AndroidManifestGenerator::AndroidManifestGenerator(Builder &b):
+       Tool(b, "AMG")
+{
+       set_run_internal(_run);
+}
+
+Target *AndroidManifestGenerator::create_target(const vector<Target *> &, const string &)
+{
+       throw logic_error("not implemented");
+}
+
+bool AndroidManifestGenerator::_run(const AndroidManifestFile &manifest)
+{
+       const Component &comp = *manifest.get_component();
+       const SourcePackage &pkg = comp.get_package();
+
+       BuildInfo binfo;
+       manifest.collect_build_info(binfo);
+
+       IO::BufferedFile out(manifest.get_path().str(), IO::M_WRITE);
+       out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+       IO::print(out, "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"%s\">\n", comp.get_name());
+       out.write("\t<uses-sdk android:minSdkVersion=\"9\" />\n");
+       // TODO Make the icon name configurable
+       bool debuggable = binfo.debug;
+       IO::print(out, "\t<application android:icon=\"@drawable/icon\" android:label=\"%s\" android:hasCode=\"false\" android:debuggable=\"%s\">\n", pkg.get_label(), debuggable);
+       if(SharedLibrary *native_lib = manifest.get_native_library())
+       {
+               out.write("\t\t<activity android:name=\"android.app.NativeActivity\"");
+               const string &orientation = manifest.get_orientation();
+               if(!orientation.empty())
+                       IO::print(out, " android:screenOrientation=\"%s\"", orientation);
+               out.write(">\n");
+               IO::print(out, "\t\t\t<meta-data android:name=\"android.app.lib_name\" android:value=\"%s\" />\n", native_lib->get_libname());
+               out.write("\t\t\t<intent-filter>\n");
+               out.write("\t\t\t\t<action android:name=\"android.intent.action.MAIN\" />\n");
+               out.write("\t\t\t\t<category android:name=\"android.intent.category.LAUNCHER\" />\n");
+               out.write("\t\t\t</intent-filter>\n");
+               out.write("\t\t</activity>\n");
+       }
+       out.write("\t</application>\n");
+       for(const string &p: manifest.get_permissions())
+               IO::print(out, "\t<uses-permission android:name=\"%s\" />\n", p);
+       out.write("</manifest>\n");
+
+       return true;
+}
diff --git a/plugins/android/androidmanifestgenerator.h b/plugins/android/androidmanifestgenerator.h
new file mode 100644 (file)
index 0000000..d1d1531
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef ANDROIDMANIFESTGENERATOR_H_
+#define ANDROIDMANIFESTGENERATOR_H_
+
+#include <msp/builder/tool.h>
+
+class AndroidManifestFile;
+
+class AndroidManifestGenerator: public Tool
+{
+public:
+       AndroidManifestGenerator(Builder &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static bool _run(const AndroidManifestFile &);
+};
+
+#endif
diff --git a/plugins/android/androidpackagefile.cpp b/plugins/android/androidpackagefile.cpp
new file mode 100644 (file)
index 0000000..00b5dad
--- /dev/null
@@ -0,0 +1,16 @@
+#include <msp/builder/component.h>
+#include <msp/builder/sourcepackage.h>
+#include "androidpackagefile.h"
+#include "androidresourcebundle.h"
+
+using namespace std;
+
+AndroidPackageFile::AndroidPackageFile(Builder &b, const Component &c, AndroidResourceBundle &resource_bundle, const vector<FileTarget *> &other_files):
+       FileTarget(b, c.get_package(), c.get_package().get_output_directory()/(c.get_name()+".apk"))
+{
+       component = &c;
+
+       add_dependency(resource_bundle);
+       for(FileTarget *f: other_files)
+               add_dependency(*f);
+}
diff --git a/plugins/android/androidpackagefile.h b/plugins/android/androidpackagefile.h
new file mode 100644 (file)
index 0000000..91b6b36
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef ANDROIDPACKAGEFILE_H_
+#define ANDROIDPACKAGEFILE_H_
+
+#include <msp/builder/filetarget.h>
+
+class AndroidResourceBundle;
+
+class AndroidPackageFile: public FileTarget
+{
+public:
+       AndroidPackageFile(Builder &, const Component &, AndroidResourceBundle &, const std::vector<FileTarget *> &);
+
+       const char *get_type() const override { return "AndroidPackageFile"; }
+};
+
+#endif
diff --git a/plugins/android/androidresourcebundle.cpp b/plugins/android/androidresourcebundle.cpp
new file mode 100644 (file)
index 0000000..5f2c43c
--- /dev/null
@@ -0,0 +1,16 @@
+#include <msp/builder/component.h>
+#include <msp/builder/sourcepackage.h>
+#include "androidmanifestfile.h"
+#include "androidresourcebundle.h"
+
+using namespace std;
+
+AndroidResourceBundle::AndroidResourceBundle(Builder &b, const Component &c, AndroidManifestFile &manifest, const vector<FileTarget *> &resources):
+       FileTarget(b, c.get_package(), c.get_package().get_temp_directory()/c.get_name()/(c.get_name()+".ap_"))
+{
+       component = &c;
+
+       add_dependency(manifest);
+       for(FileTarget *f: resources)
+               add_dependency(*f);
+}
diff --git a/plugins/android/androidresourcebundle.h b/plugins/android/androidresourcebundle.h
new file mode 100644 (file)
index 0000000..bf11509
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef ANDROIDRESOURCEBUNDLE_H_
+#define ANDROIDRESOURCEBUNDLE_H_
+
+#include <msp/builder/filetarget.h>
+
+class AndroidManifestFile;
+
+class AndroidResourceBundle: public FileTarget
+{
+public:
+       AndroidResourceBundle(Builder &, const Component &, AndroidManifestFile &, const std::vector<FileTarget *> &);
+
+       const char *get_type() const override { return "AndroidResourceBundle"; }
+};
+
+#endif
diff --git a/plugins/android/androidresourcefile.cpp b/plugins/android/androidresourcefile.cpp
new file mode 100644 (file)
index 0000000..430c0a8
--- /dev/null
@@ -0,0 +1,19 @@
+#include <msp/builder/component.h>
+#include <msp/fs/utils.h>
+#include "androidresourcefile.h"
+
+using namespace std;
+using namespace Msp;
+
+AndroidResourceFile::AndroidResourceFile(Builder &b, const Component &c, const FS::Path &p):
+       FileTarget(b, c.get_package(), p)
+{
+       string ext = FS::extpart(FS::basename(p));
+       if(ext==".png" || ext==".jpg")
+               resource_type = "drawable";
+       // TODO recognize various xml files; must inspect contents
+       else
+               resource_type = "raw";
+
+       install_location = "res/"+resource_type;
+}
diff --git a/plugins/android/androidresourcefile.h b/plugins/android/androidresourcefile.h
new file mode 100644 (file)
index 0000000..7b03287
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef ANDROIDRESOURCEFILE_H_
+#define ANDROIDRESOURCEFILE_H_
+
+#include <msp/builder/filetarget.h>
+
+/**
+A file destined to be used as a resource in an Android application.
+*/
+class AndroidResourceFile: public FileTarget
+{
+private:
+       std::string resource_type;
+
+public:
+       AndroidResourceFile(Builder &, const Component &, const Msp::FS::Path &);
+
+       const char *get_type() const override { return "AndroidResourceFile"; }
+};
+
+#endif
diff --git a/plugins/android/androidtools.cpp b/plugins/android/androidtools.cpp
new file mode 100644 (file)
index 0000000..5053682
--- /dev/null
@@ -0,0 +1,258 @@
+#include <msp/builder/architecture.h>
+#include <msp/builder/builder.h>
+#include <msp/core/algorithm.h>
+#include <msp/core/environ.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#include <msp/strings/format.h>
+#include <msp/strings/lexicalcast.h>
+#include <msp/strings/utils.h>
+#include "androidarchiver.h"
+#include "androidassetpackagingtool.h"
+#include "androidcompiler.h"
+#include "androidlinker.h"
+#include "androidmanifestgenerator.h"
+#include "androidtools.h"
+#include "apkbuilder.h"
+#include "jarsigner.h"
+
+using namespace std;
+using namespace Msp;
+
+// TODO Mark problems somewhere instead of throwing exceptions
+
+unsigned parse_version(const std::string &version_str)
+{
+       vector<string> version_parts = split(version_str, '.');
+       unsigned version = lexical_cast<unsigned>(version_parts[0])<<16;
+       if(version_parts.size()>1)
+               version += lexical_cast<unsigned>(version_parts[1])<<8;
+       if(version_parts.size()>2)
+               version += lexical_cast<unsigned>(version_parts[2]);
+       return version;
+}
+
+
+AndroidDevKit::AndroidDevKit(Builder &b, const string &type, const FS::Path &default_path):
+       builder(b)
+{
+       string var = format("ANDROID_%s_ROOT", type);
+       root = getenv(var);
+       if(root.empty())
+       {
+               if(!default_path.empty() && FS::exists(default_path))
+                       root = default_path;
+               else
+               {
+                       builder.get_logger().log("problems", "Android %s not found", type);
+                       return;
+               }
+       }
+
+       FS::Path platforms_dir = root/"platforms";
+       if(!FS::exists(platforms_dir))
+               return;
+
+       builder.get_logger().log("files", "Traversing %s", platforms_dir.str());
+       for(const string &p: list_filtered(platforms_dir, "^android-[1-9][0-9]*$"))
+       {
+               unsigned api = lexical_cast<unsigned>(p.substr(8));
+               if(api>63)
+                       builder.get_logger().log("problems", "API level %d is too high", api);
+               else
+                       supported_api_levels |= static_cast<uint64_t>(1)<<api;
+       }
+}
+
+void AndroidDevKit::select_api_level(unsigned api)
+{
+       if(!((supported_api_levels>>api)&1))
+               throw invalid_argument("AndroidDevKit::select_api_level");
+
+       init_api_level(api);
+}
+
+
+AndroidSdk::AndroidSdk(Builder &b):
+       AndroidDevKit(b, "SDK")
+{
+       find_build_tools_dir();
+}
+
+void AndroidSdk::find_build_tools_dir()
+{
+       FS::Path bt_dir = root/"build-tools";
+       if(!FS::exists(bt_dir))
+       {
+               builder.get_logger().log("problems", "Android build-tools not found");
+               return;
+       }
+
+       builder.get_logger().log("files", "Traversing %s", bt_dir.str());
+       string use_tools;
+       unsigned latest_version = 0;
+       for(const string &v: list_files(bt_dir))
+       {
+               unsigned version = parse_version(v);
+               if(version>latest_version)
+               {
+                       use_tools = v;
+                       latest_version = version;
+               }
+       }
+
+       if(use_tools.empty())
+       {
+               builder.get_logger().log("problems", "Android build-tools not found");
+               return;
+       }
+
+       build_tools_dir = bt_dir/use_tools;
+       builder.get_logger().log("tools", "Android build-tools found in %s", build_tools_dir.str());
+}
+
+void AndroidSdk::init_api_level(unsigned api)
+{
+       platform_jar = root/"platforms"/format("android-%d", api)/"android.jar";
+}
+
+
+AndroidNdk::AndroidNdk(Builder &b, const Architecture &a, const AndroidSdk &sdk):
+       AndroidDevKit(b, "NDK", create_default_path(sdk)),
+       architecture(a)
+{
+       if(!root.empty())
+       {
+               FS::Path csr = root/"sysroot";
+               if(FS::exists(csr))
+               {
+                       common_sysroot = csr;
+                       builder.get_logger().log("tools", "Android NDK common sysroot is %s", common_sysroot);
+               }
+       }
+
+       find_toolchain_dir();
+}
+
+FS::Path AndroidNdk::create_default_path(const AndroidSdk &sdk)
+{
+       if(sdk.get_root_dir().empty())
+               return FS::Path();
+       return sdk.get_root_dir()/"ndk-bundle";
+}
+
+void AndroidNdk::find_toolchain_dir()
+{
+       if(root.empty())
+               return;
+
+       FS::Path toolchains_dir = root/"toolchains";
+       if(!FS::exists(toolchains_dir))
+       {
+               builder.get_logger().log("problems", "Android NDK toolchains not found");
+               return;
+       }
+
+       builder.get_logger().log("files", "Traversing %s", toolchains_dir.str());
+       string prefix = architecture.get_cross_prefix()+"-";
+       string use_toolchain;
+       unsigned latest_version = 0;
+       for(const string &t: list_filtered(toolchains_dir, "^"+prefix))
+       {
+               string version_str = t.substr(prefix.size());
+               string compiler = "gcc";
+               if(!isdigit(version_str[0]))
+               {
+                       unsigned j;
+                       for(j=1; (j<version_str.size() && !isdigit(version_str[j])); ++j) ;
+                       compiler = version_str.substr(0, j);
+                       version_str = version_str.substr(j);
+               }
+
+               if(compiler!="gcc")
+                       continue;
+
+               unsigned version = parse_version(version_str);
+               if(version>latest_version)
+               {
+                       use_toolchain = t;
+                       latest_version = version;
+               }
+       }
+
+       if(use_toolchain.empty())
+       {
+               builder.get_logger().log("problems", "No applicable Android NDK toolchains found");
+               return;
+       }
+
+       const Architecture &native_arch = builder.get_native_arch();
+
+       FS::Path tc_archs_dir = toolchains_dir/use_toolchain/"prebuilt";
+       builder.get_logger().log("files", "Traversing %s", tc_archs_dir.str());
+       string use_arch = native_arch.best_match(list_files(tc_archs_dir));
+
+       if(use_arch.empty())
+       {
+               builder.get_logger().log("problems", "No applicable Android NDK toolchains found");
+               return;
+       }
+
+       bin_dir = toolchains_dir/use_toolchain/"prebuilt"/use_arch/"bin";
+       builder.get_logger().log("tools", "Android NDK toolchain binaries found in %s", bin_dir.str());
+}
+
+void AndroidNdk::init_api_level(unsigned api)
+{
+       FS::Path platform_archs_dir = root/"platforms"/format("android-%d", api);
+       builder.get_logger().log("files", "Traversing %s", platform_archs_dir.str());
+       vector<string> platform_archs = list_filtered(platform_archs_dir, "^arch-");
+       for(string &a: platform_archs)
+               a.erase(0, 5);
+       string use_arch = architecture.best_match(platform_archs);
+
+       if(use_arch.empty())
+       {
+               builder.get_logger().log("problems", "No matching Android NDK platform found");
+               return;
+       }
+
+       platform_sysroot = platform_archs_dir/("arch-"+use_arch);
+       builder.get_logger().log("tools", "Android NDK platform sysroot is %s", platform_sysroot);
+}
+
+
+AndroidTools::AndroidTools(Builder &builder, const Architecture &arch):
+       Toolchain("android-gnu", get_priority(arch)),
+       sdk(builder),
+       ndk(builder, arch, sdk)
+{
+       if(uint64_t common_api_levels = sdk.get_supported_api_levels()&ndk.get_supported_api_levels())
+       {
+               unsigned api = 0;
+               for(unsigned i=32; i>0; i>>=1)
+                       api += i*!!(common_api_levels>>(api+i));
+               builder.get_logger().log("tools", "Using Android API level %d", api);
+               sdk.select_api_level(api);
+               ndk.select_api_level(api);
+       }
+       else
+               builder.get_logger().log("problems", "No usable Android platforms found");
+
+       add_tool(new AndroidCompiler(builder, arch, "CC", ndk));
+       add_tool(new AndroidCompiler(builder, arch, "CXX", ndk));
+       add_tool(new AndroidLinker(builder, arch, ndk));
+       add_tool(new AndroidArchiver(builder, arch, ndk));
+       add_tool(new AndroidManifestGenerator(builder));
+       add_tool(new AndroidAssetPackagingTool(builder, sdk));
+       add_tool(new ApkBuilder(builder));
+       add_tool(new JarSigner(builder));
+}
+
+int AndroidTools::get_priority(const Architecture &arch)
+{
+       if(arch.get_system()=="android")
+               return 30;
+       else
+               return -10;
+}
diff --git a/plugins/android/androidtools.h b/plugins/android/androidtools.h
new file mode 100644 (file)
index 0000000..a5fd560
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef ANDROIDTOOLS_H_
+#define ANDROIDTOOLS_H_
+
+#include <cstdint>
+#include <msp/builder/toolchain.h>
+#include <msp/fs/path.h>
+
+class Architecture;
+class Builder;
+
+class AndroidDevKit
+{
+protected:
+       Builder &builder;
+       Msp::FS::Path root;
+       /* Needs refactoring if API levels go over 63.  At present rate this will
+       take decades to occur. */
+       uint64_t supported_api_levels = 0;
+
+       AndroidDevKit(Builder &, const std::string &, const Msp::FS::Path & = Msp::FS::Path());
+       ~AndroidDevKit() { }
+
+public:
+       const Msp::FS::Path &get_root_dir() const { return root; }
+       uint64_t get_supported_api_levels() const { return supported_api_levels; }
+       void select_api_level(unsigned);
+protected:
+       virtual void init_api_level(unsigned) = 0;
+};
+
+class AndroidSdk: public AndroidDevKit
+{
+private:
+       Msp::FS::Path build_tools_dir;
+       // TODO use a FileTarget for the jar
+       Msp::FS::Path platform_jar;
+
+public:
+       AndroidSdk(Builder &);
+
+private:
+       void find_build_tools_dir();
+       void init_api_level(unsigned) override;
+
+public:
+       const Msp::FS::Path &get_build_tools_dir() const { return build_tools_dir; }
+       const Msp::FS::Path &get_platform_jar() const { return platform_jar; }
+};
+
+class AndroidNdk: public AndroidDevKit
+{
+private:
+       const Architecture &architecture;
+       Msp::FS::Path bin_dir;
+       Msp::FS::Path common_sysroot;
+       Msp::FS::Path platform_sysroot;
+
+public:
+       AndroidNdk(Builder &, const Architecture &, const AndroidSdk &);
+private:
+       static Msp::FS::Path create_default_path(const AndroidSdk &);
+
+       void find_toolchain_dir();
+       void init_api_level(unsigned) override;
+
+public:
+       const Msp::FS::Path &get_bin_dir() const { return bin_dir; }
+       const Msp::FS::Path &get_common_sysroot() const { return common_sysroot; }
+       const Msp::FS::Path &get_platform_sysroot() const { return platform_sysroot; }
+};
+
+
+class AndroidTools: public Toolchain
+{
+private:
+       AndroidSdk sdk;
+       AndroidNdk ndk;
+
+public:
+       AndroidTools(Builder &, const Architecture &);
+
+       static int get_priority(const Architecture &);
+};
+
+#endif
diff --git a/plugins/android/apkbuilder.cpp b/plugins/android/apkbuilder.cpp
new file mode 100644 (file)
index 0000000..ec20854
--- /dev/null
@@ -0,0 +1,82 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/chainedtask.h>
+#include <msp/builder/component.h>
+#include <msp/builder/externaltask.h>
+#include <msp/builder/filetarget.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/fs/utils.h>
+#include "androidpackagefile.h"
+#include "androidresourcebundle.h"
+#include "apkbuilder.h"
+
+using namespace std;
+using namespace Msp;
+
+// TODO Separate jar into its own tool and have this one just chain the two
+
+ApkBuilder::ApkBuilder(Builder &b):
+       Tool(b, "APK")
+{
+       set_command("jar");
+       set_run(_run);
+}
+
+Target *ApkBuilder::create_target(const vector<Target *> &sources, const string &)
+{
+       AndroidResourceBundle *resource_bundle = 0;
+       vector<FileTarget *> other_files;
+       other_files.reserve(sources.size());
+       for(Target *s: sources)
+       {
+               if(AndroidResourceBundle *r = dynamic_cast<AndroidResourceBundle *>(s))
+                       resource_bundle = r;
+               else if(FileTarget *f = dynamic_cast<FileTarget *>(s))
+                       other_files.push_back(f);
+       }
+       AndroidPackageFile *apk = new AndroidPackageFile(builder, *resource_bundle->get_component(), *resource_bundle, other_files);
+       apk->set_tool(*this);
+       return apk;
+}
+
+void ApkBuilder::do_prepare(ToolData &tool) const
+{
+       Tool *jarsigner = &builder.get_toolchain().get_tool("JSGN");
+       jarsigner->prepare();
+       tool.extra_data = jarsigner;
+}
+
+Task *ApkBuilder::_run(const Target &tgt)
+{
+       const AndroidPackageFile &apk = dynamic_cast<const AndroidPackageFile &>(tgt);
+       const ApkBuilder &tool = dynamic_cast<const ApkBuilder &>(*apk.get_tool());
+
+       ExternalTask::Arguments argv;
+       argv.push_back(tool.get_executable()->get_path().str());
+       argv.push_back("u");
+
+       FS::Path input_path;
+       vector<FS::Path> files;
+       files.reserve(apk.get_dependencies().size());
+       for(Target *d: apk.get_dependencies())
+       {
+               FileTarget *file = dynamic_cast<FileTarget *>(d);
+               Target *real = d->get_real_target();
+
+               if(dynamic_cast<AndroidResourceBundle *>(real))
+                       input_path = file->get_path();
+               else if(real->get_package()==apk.get_package())
+                       files.push_back(file->get_path());
+       }
+
+       FS::Path work_dir = FS::dirname(input_path);
+
+       for(const FS::Path &f: files)
+               argv.push_back(FS::relative(f, work_dir).str());
+
+       ExternalTask *task = new ExternalTask(argv, work_dir);
+       task->set_stdin(FS::basename(input_path));
+       task->set_stdout(FS::relative(apk.get_path(), work_dir));
+       ChainedTask *chain = new ChainedTask(task);
+       chain->add_task(tool.extra_data.value<Tool *>()->run(apk));
+       return chain;
+}
diff --git a/plugins/android/apkbuilder.h b/plugins/android/apkbuilder.h
new file mode 100644 (file)
index 0000000..b4e534f
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef APKBUILDER_H_
+#define APKBUILDER_H_
+
+#include <msp/builder/tool.h>
+
+class AndroidPackageFile;
+
+class ApkBuilder: public Tool
+{
+public:
+       ApkBuilder(Builder &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+protected:
+       void do_prepare(ToolData &) const override;
+
+private:
+       static Task *_run(const Target &);
+};
+
+#endif
diff --git a/plugins/android/jarsigner.cpp b/plugins/android/jarsigner.cpp
new file mode 100644 (file)
index 0000000..9738351
--- /dev/null
@@ -0,0 +1,41 @@
+#include <msp/builder/component.h>
+#include <msp/builder/filetarget.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/core/environ.h>
+#include <msp/fs/utils.h>
+#include "jarsigner.h"
+
+using namespace std;
+using namespace Msp;
+
+JarSigner::JarSigner(Builder &b):
+       Tool(b, "JSGN")
+{
+       set_command("jarsigner");
+       set_run_external(_run);
+}
+
+Target *JarSigner::create_target(const vector<Target *> &, const string &)
+{
+       throw logic_error("not implemented");
+}
+
+ExternalTask::Arguments JarSigner::_run(const FileTarget &file, FS::Path &work_dir)
+{
+       const Tool &tool = *file.get_tool();
+
+       ExternalTask::Arguments argv;
+       argv.push_back(tool.get_executable()->get_path().str());
+
+       // TODO Make this generic
+       FS::Path home_dir = Msp::getenv("HOME");
+       argv.push_back("-keystore");
+       argv.push_back((home_dir/".android"/"debug.keystore").str());
+       argv.push_back("-storepass");
+       argv.push_back("android");
+
+       argv.push_back(FS::relative(file.get_path(), work_dir).str());
+       argv.push_back("androiddebugkey");
+
+       return argv;
+}
diff --git a/plugins/android/jarsigner.h b/plugins/android/jarsigner.h
new file mode 100644 (file)
index 0000000..6c97373
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef JARSIGNER_H_
+#define JARSIGNER_H_
+
+#include <msp/builder/tool.h>
+
+class FileTarget;
+
+class JarSigner: public Tool
+{
+public:
+       JarSigner(Builder &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static ExternalTask::Arguments _run(const FileTarget &, Msp::FS::Path &);
+};
+
+#endif
diff --git a/plugins/builtin/builtintools.cpp b/plugins/builtin/builtintools.cpp
new file mode 100644 (file)
index 0000000..c2c11e6
--- /dev/null
@@ -0,0 +1,17 @@
+#include "builtintools.h"
+#include "copy.h"
+#include "compilecommandsgenerator.h"
+#include "pkgconfiggenerator.h"
+#include "tar.h"
+#include "vcxprojectgenerator.h"
+#include "vssolutiongenerator.h"
+
+BuiltinTools::BuiltinTools(Builder &builder)
+{
+       add_tool(new Copy(builder));
+       add_tool(new Tar(builder));
+       add_tool(new PkgConfigGenerator(builder));
+       add_tool(new VcxProjectGenerator(builder));
+       add_tool(new VsSolutionGenerator(builder));
+       add_tool(new CompileCommandsGenerator(builder));
+}
diff --git a/plugins/builtin/builtintools.h b/plugins/builtin/builtintools.h
new file mode 100644 (file)
index 0000000..4771e83
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef BUILTINTOOLS_H_
+#define BUILTINTOOLS_H_
+
+#include <msp/builder/toolchain.h>
+
+class Builder;
+
+class BuiltinTools: public Toolchain
+{
+public:
+       BuiltinTools(Builder &);
+};
+
+#endif
diff --git a/plugins/builtin/compilecommandsgenerator.cpp b/plugins/builtin/compilecommandsgenerator.cpp
new file mode 100644 (file)
index 0000000..61e11db
--- /dev/null
@@ -0,0 +1,54 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/objectfile.h>
+#include <msp/builder/sourcefile.h>
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include <msp/strings/utils.h>
+#include "compilecommandsgenerator.h"
+#include "compilecommandsjson.h"
+
+using namespace std;
+using namespace Msp;
+
+CompileCommandsGenerator::CompileCommandsGenerator(Builder &b):
+       Tool(b, "CCJG")
+{
+       set_run_internal(_run);
+}
+
+Target *CompileCommandsGenerator::create_target(const vector<Target *> &, const string &)
+{
+       throw logic_error("Not implemented");
+}
+
+bool CompileCommandsGenerator::_run(const CompileCommandsJson &cmds)
+{
+       Builder &builder = cmds.get_package()->get_builder();
+       const SourcePackage &spkg = *cmds.get_package();
+       string work_dir = c_escape(spkg.get_source_directory().str());
+
+       IO::BufferedFile out(cmds.get_path().str(), IO::M_WRITE);
+       IO::print(out, "[");
+
+       bool first = true;
+       for(const auto &kvp: builder.get_build_graph().get_targets())
+               if(kvp.second->is_buildable() && kvp.second->get_package()==&spkg)
+                       if(ObjectFile *obj = dynamic_cast<ObjectFile *>(kvp.second))
+                       {
+                               FS::Path src_path = obj->get_source().get_path();
+                               Task *task = kvp.second->build();
+                               if(!first)
+                                       out.put(',');
+                               IO::print(out, "\n\t{\n");
+                               IO::print(out, "\t\t\"file\": \"%s\"\n", src_path);
+                               IO::print(out, "\t\t\"command\": \"%s\"\n", c_escape(task->get_command()));
+                               IO::print(out, "\t\t\"directory\": \"%s\"\n", work_dir);
+                               IO::print(out, "\t}");
+                               delete task;
+                               first = false;
+                       }
+
+       IO::print(out, "\n]\n");
+
+       return true;
+}
diff --git a/plugins/builtin/compilecommandsgenerator.h b/plugins/builtin/compilecommandsgenerator.h
new file mode 100644 (file)
index 0000000..ec4a501
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef COMPILECOMMANDSGENERATOR_H_
+#define COMPILECOMMANDSGENERATOR_H_
+
+#include <msp/builder/tool.h>
+
+class CompileCommandsJson;
+
+class CompileCommandsGenerator: public Tool
+{
+public:
+       CompileCommandsGenerator(Builder &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static bool _run(const CompileCommandsJson &);
+};
+
+#endif
diff --git a/plugins/builtin/compilecommandsjson.cpp b/plugins/builtin/compilecommandsjson.cpp
new file mode 100644 (file)
index 0000000..d4adc14
--- /dev/null
@@ -0,0 +1,17 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/package.h>
+#include <msp/builder/objectfile.h>
+#include "compilecommandsjson.h"
+
+CompileCommandsJson::CompileCommandsJson(Builder &b, const SourcePackage &p):
+       FileTarget(b, p, p.get_source_directory()/("compile_commands.json"))
+{
+       tool = &builder.get_toolchain().get_tool("CCJG");
+}
+
+void CompileCommandsJson::find_dependencies()
+{
+       for(const auto &kvp: builder.get_build_graph().get_targets())
+               if(kvp.second->is_buildable() && kvp.second->get_package()==package && dynamic_cast<ObjectFile *>(kvp.second))
+                       kvp.second->prepare();
+}
diff --git a/plugins/builtin/compilecommandsjson.h b/plugins/builtin/compilecommandsjson.h
new file mode 100644 (file)
index 0000000..be9c448
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef COMPILECOMMANDSJSON_H_
+#define COMPILECOMMANDSJSON_H_
+
+#include <msp/builder/sourcepackage.h>
+#include <msp/builder/filetarget.h>
+
+class CompileCommandsJson: public FileTarget
+{
+private:
+       
+public:
+       CompileCommandsJson(Builder &, const SourcePackage &);
+
+       const char *get_type() const override { return "CompileCommandsJson"; }
+
+protected:
+       void find_dependencies() override;
+};
+
+#endif
diff --git a/plugins/builtin/copy.cpp b/plugins/builtin/copy.cpp
new file mode 100644 (file)
index 0000000..0633ff9
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef _WIN32
+#include <unistd.h>
+#include <sys/stat.h>
+#endif
+#include <msp/builder/builder.h>
+#include <msp/builder/installedfile.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include "copy.h"
+
+using namespace std;
+using namespace Msp;
+
+Copy::Copy(Builder &b):
+       Tool(b, "CP")
+{
+       set_run_internal(_run);
+}
+
+Target *Copy::create_target(const vector<Target *> &sources, const string &arg)
+{
+       FileTarget &file_tgt = dynamic_cast<FileTarget &>(*sources.front());
+       InstalledFile *inst = new InstalledFile(builder, *file_tgt.get_package(), file_tgt, arg);
+       inst->set_tool(*this);
+       return inst;
+}
+
+bool Copy::_run(const InstalledFile &install)
+{
+       const FileTarget &source = install.get_source();
+       const FS::Path &src_path = source.get_path();
+       const FS::Path &dst_path = install.get_path();
+
+       try
+       {
+               IO::File in(src_path.str());
+               IO::File out(dst_path.str(), IO::M_WRITE);
+
+               // Actual transfer loop
+               char buf[16384];
+               while(!in.eof())
+               {
+                       unsigned len = in.read(buf, sizeof(buf));
+                       out.write(buf, len);
+               }
+       }
+       catch(const exception &e)
+       {
+               IO::print(IO::cerr, "%s\n", e.what());
+               return false;
+       }
+
+#ifndef _WIN32
+       // Preserve file permissions
+       struct stat st;
+       if(stat(src_path.str().c_str(), &st)==0)
+               chmod(dst_path.str().c_str(), st.st_mode&0777);
+
+       const FS::Path &link = install.get_symlink();
+       if(!link.empty())
+       {
+               FS::Path relpath = FS::relative(dst_path, FS::dirname(link));
+               if(FS::exists(link))
+                       FS::unlink(link);
+               symlink(relpath.str().c_str(), link.str().c_str());
+       }
+#endif
+
+       return true;
+}
diff --git a/plugins/builtin/copy.h b/plugins/builtin/copy.h
new file mode 100644 (file)
index 0000000..f6f01fb
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef COPY_H_
+#define COPY_H_
+
+#include <msp/builder/tool.h>
+
+class InstalledFile;
+
+/**
+Copies a file to another place.  Used by the InstalledFile target.
+*/
+class Copy: public Tool
+{
+public:
+       Copy(Builder &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static bool _run(const InstalledFile &);
+};
+
+#endif
diff --git a/plugins/builtin/pkgconfigfile.cpp b/plugins/builtin/pkgconfigfile.cpp
new file mode 100644 (file)
index 0000000..70ab59d
--- /dev/null
@@ -0,0 +1,11 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/package.h>
+#include "pkgconfigfile.h"
+
+PkgConfigFile::PkgConfigFile(Builder &b, const SourcePackage &p):
+       FileTarget(b, p, p.get_source_directory()/(p.get_name()+".pc"))
+{
+       tool = &builder.get_toolchain().get_tool("PCG");
+
+       install_location = "lib/pkgconfig";
+}
diff --git a/plugins/builtin/pkgconfigfile.h b/plugins/builtin/pkgconfigfile.h
new file mode 100644 (file)
index 0000000..23027e4
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef PKGCONFIGFILE_H_
+#define PKGCONFIGFILE_H_
+
+#include <msp/builder/sourcepackage.h>
+#include <msp/builder/filetarget.h>
+
+/**
+Creates a .pc file to enable other packages fetch build options with pkg-config.
+*/
+class PkgConfigFile: public FileTarget
+{
+public:
+       PkgConfigFile(Builder &, const SourcePackage &);
+
+       const char *get_type() const override { return "PkgConfigFile"; }
+};
+
+#endif
diff --git a/plugins/builtin/pkgconfiggenerator.cpp b/plugins/builtin/pkgconfiggenerator.cpp
new file mode 100644 (file)
index 0000000..76ed7d9
--- /dev/null
@@ -0,0 +1,73 @@
+#include <msp/builder/builder.h>
+#include <msp/fs/utils.h>
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include "pkgconfigfile.h"
+#include "pkgconfiggenerator.h"
+
+using namespace std;
+using namespace Msp;
+
+PkgConfigGenerator::PkgConfigGenerator(Builder &b):
+       Tool(b, "PCG")
+{
+       set_run_internal(_run);
+}
+
+Target *PkgConfigGenerator::create_target(const vector<Target *> &, const string &)
+{
+       throw logic_error("Not implemented");
+}
+
+bool PkgConfigGenerator::_run(const PkgConfigFile &pkgc)
+{
+       Builder &builder = pkgc.get_package()->get_builder();
+       const SourcePackage &spkg = *pkgc.get_package();
+
+       IO::BufferedFile out(pkgc.get_path().str(), IO::M_WRITE);
+       IO::print(out, "prefix=%s\n", builder.get_prefix().str());
+       IO::print(out, "source=%s\n\n", spkg.get_source_directory());
+
+       IO::print(out, "Name: %s\n", spkg.get_label());
+       IO::print(out, "Description: %s\n", spkg.get_description());
+       IO::print(out, "Version: %s\n", spkg.get_version());
+
+       IO::print(out, "Requires:");
+       for(const Package *r: spkg.get_required_packages())
+               if(r->uses_pkgconfig())
+                       IO::print(out, " %s", r->get_name());
+       out.put('\n');
+
+       const BuildInfo &binfo = spkg.get_exported_build_info();
+       IO::print(out, "Libs:");
+       for(const FS::Path &p: binfo.libpath)
+               IO::print(out, " -L%s", prefixify(p, builder.get_prefix()));
+       for(const string &l: binfo.libs)
+               IO::print(out, " -l%s", l);
+       if(binfo.threads)
+               out.write("-pthread");
+       out.put('\n');
+
+       IO::print(out, "Cflags:");
+       for(const FS::Path &p: binfo.incpath)
+               IO::print(out, " -I%s", prefixify(p, builder.get_prefix()));
+       for(const auto &kvp: binfo.defines)
+               if(kvp.second.empty())
+                       IO::print(out, " -D%s", kvp.first);
+               else
+                       IO::print(out, " -D%s=%s", kvp.first, kvp.second);
+       out.put('\n');
+
+       return true;
+}
+
+string PkgConfigGenerator::prefixify(const FS::Path &path, const FS::Path &prefix)
+{
+       if(FS::descendant_depth(path, prefix)>=0)
+       {
+               FS::Path rel_path = FS::relative(path, prefix);
+               return "${prefix}"+rel_path.str().substr(1);
+       }
+       else
+               return path.str();
+}
diff --git a/plugins/builtin/pkgconfiggenerator.h b/plugins/builtin/pkgconfiggenerator.h
new file mode 100644 (file)
index 0000000..2a0e0b1
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef PKGCONFIGGENERATOR_H_
+#define PKGCONFIGGENERATOR_H_
+
+#include <msp/builder/tool.h>
+
+class PkgConfigFile;
+
+class PkgConfigGenerator: public Tool
+{
+public:
+       PkgConfigGenerator(Builder &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static bool _run(const PkgConfigFile &);
+       static std::string prefixify(const Msp::FS::Path &, const Msp::FS::Path &);
+};
+
+#endif
diff --git a/plugins/builtin/tar.cpp b/plugins/builtin/tar.cpp
new file mode 100644 (file)
index 0000000..4bac32a
--- /dev/null
@@ -0,0 +1,94 @@
+#include <cstring>
+#include <msp/builder/builder.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include "tar.h"
+#include "tarball.h"
+
+using namespace std;
+using namespace Msp;
+
+Tar::Tar(Builder &b):
+       Tool(b, "TAR")
+{
+       processing_unit = COMPONENT;
+       set_run_internal(&_run);
+}
+
+Target *Tar::create_target(const vector<Target *> &sources, const string &arg)
+{
+       if(sources.empty() || !sources.front()->get_package())
+               throw invalid_argument("Tar::create_target");
+
+       TarBall *tarball = new TarBall(builder, *sources.front()->get_package(), arg);
+       for(Target *s: sources)
+               tarball->add_dependency(*s);
+
+       tarball->set_tool(*this);
+
+       return tarball;
+}
+
+bool Tar::_run(const TarBall &tarball)
+{
+       const FS::Path &pkg_src = tarball.get_package()->get_source_directory();
+       FS::Path basedir = FS::basepart(FS::basename(tarball.get_path()));
+
+       IO::File out(tarball.get_path().str(), IO::M_WRITE);
+       for(Target *d: tarball.get_dependencies())
+       {
+               FileTarget *ft = dynamic_cast<FileTarget *>(d);
+               if(!ft)
+                       continue;
+
+               char buf[4096];
+               memset(buf, 0, 512);
+
+               string rel_path = (basedir/relative(ft->get_path(), pkg_src)).str();
+               if(rel_path.size()>99)
+               {
+                       IO::print("Can't store %s in tar archive - too long name\n", rel_path);
+                       return false;
+               }
+
+               memcpy(buf, rel_path.data(), rel_path.size());
+
+               FS::Stat st = FS::stat(ft->get_path());
+               store_number(buf+100, 0666, 7);
+               store_number(buf+108, 0, 7);
+               store_number(buf+116, 0, 7);
+               store_number(buf+124, st.get_size(), 11);
+               store_number(buf+136, st.get_modify_time().to_unixtime(), 11);
+               buf[156] = '0';
+
+               memset(buf+148, ' ', 8);
+               unsigned chk = 0;
+               for(unsigned j=0; j<512; ++j)
+                       chk += static_cast<unsigned char>(buf[j]);
+               store_number(buf+148, chk, 7);
+               buf[155] = 0;
+
+               out.write(buf, 512);
+               IO::File in(ft->get_path().str());
+               for(unsigned j=0; j<st.get_size(); j+=4096)
+               {
+                       unsigned len = in.read(buf, 4096);
+                       len += ((~len)+1)&0777;
+                       out.write(buf, len);
+               }
+       }
+
+       return true;
+}
+
+void Tar::store_number(char *buf, unsigned value, unsigned length)
+{
+       for(unsigned i=length; i--;)
+       {
+               buf[i] = '0'+value%8;
+               value /= 8;
+       }
+}
diff --git a/plugins/builtin/tar.h b/plugins/builtin/tar.h
new file mode 100644 (file)
index 0000000..9011296
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef TAR_H_
+#define TAR_H_
+
+#include <msp/builder/tool.h>
+
+class TarBall;
+
+class Tar: public Tool
+{
+public:
+       Tar(Builder &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static bool _run(const TarBall &);
+       static void store_number(char *, unsigned, unsigned);
+};
+
+#endif
diff --git a/plugins/builtin/tarball.cpp b/plugins/builtin/tarball.cpp
new file mode 100644 (file)
index 0000000..0b2d481
--- /dev/null
@@ -0,0 +1,14 @@
+#include <msp/builder/sourcepackage.h>
+#include "tar.h"
+#include "tarball.h"
+
+using namespace std;
+
+TarBall::TarBall(Builder &b, const SourcePackage &p, const string &n):
+       FileTarget(b, p, p.get_source_directory()/(n+".tar"))
+{ }
+
+const SourcePackage *TarBall::get_package() const
+{
+       return static_cast<const SourcePackage *>(package);
+}
diff --git a/plugins/builtin/tarball.h b/plugins/builtin/tarball.h
new file mode 100644 (file)
index 0000000..f63840e
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef TARBALL_H_
+#define TARBALL_H_
+
+#include <msp/builder/filetarget.h>
+
+class TarBall: public FileTarget
+{
+public:
+       TarBall(Builder &, const SourcePackage &, const std::string &);
+
+       const char *get_type() const override { return "TarBall"; }
+       const SourcePackage *get_package() const;
+};
+
+#endif
diff --git a/plugins/builtin/vcxprojectfile.cpp b/plugins/builtin/vcxprojectfile.cpp
new file mode 100644 (file)
index 0000000..3ec7da1
--- /dev/null
@@ -0,0 +1,24 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/crypto/md5.h>
+#include <msp/strings/format.h>
+#include "vcxprojectfile.h"
+
+using namespace Msp;
+
+VcxProjectFile::VcxProjectFile(Builder &b, const SourcePackage &p):
+       FileTarget(b, p, p.get_source_directory()/(p.get_name()+".vcxproj"))
+{
+       tool = &builder.get_toolchain().get_tool("VCXG");
+
+       char digest[16];
+       Crypto::MD5(package->get_name()).get_digest(digest, sizeof(digest));
+       digest[6] = 3;
+       digest[8] = (digest[6]&0x3F)|0x80;
+       for(unsigned j=0; j<sizeof(digest); ++j)
+       {
+               if(j==4 || j==6 || j==8 || j==10)
+                       guid += '-';
+               guid += format("%02x", static_cast<unsigned char>(digest[j]));
+       }
+}
diff --git a/plugins/builtin/vcxprojectfile.h b/plugins/builtin/vcxprojectfile.h
new file mode 100644 (file)
index 0000000..1f85d0e
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef VCXPROJECTFILE_H_
+#define VCXPROJECTFILE_H_
+
+#include <msp/builder/filetarget.h>
+
+class VcxProjectFile: public FileTarget
+{
+private:
+       std::string guid;
+
+public:
+       VcxProjectFile(Builder &, const SourcePackage &);
+
+       const char *get_type() const override { return "VcxProjectFile"; }
+
+       const std::string &get_guid() const { return guid; }
+};
+
+#endif
diff --git a/plugins/builtin/vcxprojectgenerator.cpp b/plugins/builtin/vcxprojectgenerator.cpp
new file mode 100644 (file)
index 0000000..7cb9faf
--- /dev/null
@@ -0,0 +1,136 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/csourcefile.h>
+#include <msp/builder/executable.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/core/application.h>
+#include <msp/fs/utils.h>
+#include <msp/io/print.h>
+#include <msp/strings/utils.h>
+#include "vcxprojectfile.h"
+#include "vcxprojectgenerator.h"
+
+using namespace std;
+using namespace Msp;
+
+VcxProjectGenerator::VcxProjectGenerator(Builder &b):
+       Tool(b, "VCXG")
+{
+       set_run_internal(_run);
+}
+
+Target *VcxProjectGenerator::create_target(const vector<Target *> &, const string &)
+{
+       throw logic_error("Not implemented");
+}
+
+bool VcxProjectGenerator::_run(const VcxProjectFile &project)
+{
+       const SourcePackage &spkg = *project.get_package();
+       Builder &builder = spkg.get_builder();
+
+       IO::BufferedFile out(project.get_path().str(), IO::M_WRITE);
+       IO::print(out, "<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n");
+
+       IO::print(out, "\t<ItemGroup Label=\"ProjectConfigurations\">\n");
+       vector<string> build_types = builder.get_build_types();
+       const char *platforms[] = { "Win32", "x64" };
+       for(const char *p: platforms)
+               for(const string &b: build_types)
+               {
+                       IO::print(out, "\t\t<ProjectConfiguration Include=\"%s|%s\">\n", b, p);
+                       IO::print(out, "\t\t\t<Configuration>%s</Configuration>\n", b);
+                       IO::print(out, "\t\t\t<Platform>%s</Platform>\n", p);
+                       IO::print(out, "\t\t</ProjectConfiguration>\n");
+               }
+       IO::print(out, "\t</ItemGroup>\n");
+
+       IO::print(out, "\t<PropertyGroup Label=\"Globals\">\n");
+       IO::print(out, "\t\t<VCProjectVersion>15.0</VCProjectVersion>\n");
+       IO::print(out, "\t\t<Keyword>MakeFileProj</Keyword>\n");
+       IO::print(out, "\t\t<ProjectGuid>{%s}</ProjectGuid>\n", project.get_guid());
+       IO::print(out, "\t</PropertyGroup>\n");
+
+       IO::print(out, "\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n");
+
+       const Executable *exe = 0;
+       for(const Target *t: builder.get_build_graph().get_target("world")->get_dependencies())
+               if(t->get_package()==&spkg)
+                       if((exe = dynamic_cast<const Executable *>(t)))
+                               break;
+
+       const char *argv0 = Application::get_argv0();
+       const string &toolchain = builder.get_current_arch().get_toolchain();
+       for(const char *p: platforms)
+               for(const string &b: build_types)
+               {
+                       string base_cmd = format("%s --arch=%s-%s --build-type=%s --prefix=%s", argv0, p, toolchain, b, builder.get_prefix());
+                       IO::print(out, "\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='%s|%s'\" Label=\"Configuration\">\n", b, p);
+                       IO::print(out, "\t\t<ConfigurationType>MakeFile</ConfigurationType>\n");
+                       IO::print(out, "\t\t<NMakeBuildCommandLine>%s</NMakeBuildCommandLine>\n", base_cmd);
+                       IO::print(out, "\t\t<NMakeCleanCommandLine>%s -c</NMakeCleanCommandLine>\n", base_cmd);
+                       IO::print(out, "\t\t<NMakeReBuildCommandLine>%s -B</NMakeReBuildCommandLine>\n", base_cmd);
+                       if(exe)
+                               IO::print(out, "\t\t<NMakeOutput>%s</NMakeOutput>\n", exe->get_path());
+                       IO::print(out, "\t</PropertyGroup>\n");
+               }
+
+       IO::print(out, "\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n");
+
+       vector<const FileTarget *> sources;
+       vector<const FileTarget *> includes;
+       vector<const FileTarget *> others;
+       BuildInfo build_info;
+       for(const auto &kvp: builder.get_build_graph().get_targets())
+               if(kvp.second->get_package()==&spkg)
+               {
+                       if(kvp.second->is_buildable())
+                       {
+                               BuildInfo tgt_binfo;
+                               kvp.second->collect_build_info(tgt_binfo);
+                               build_info.update_from(tgt_binfo, BuildInfo::CHAINED);
+                       }
+                       else if(const FileTarget *file = dynamic_cast<const FileTarget *>(kvp.second))
+                       {
+                               if(dynamic_cast<const CSourceFile *>(file))
+                               {
+                                       string ext = tolower(FS::extpart(FS::basename(file->get_path())));
+                                       if(ext==".h" || ext==".hpp")
+                                               includes.push_back(file);
+                                       else
+                                               sources.push_back(file);
+                               }
+                               else
+                                       others.push_back(file);
+                       }
+               }
+
+       if(!build_info.incpath.empty())
+       {
+               IO::print(out, "\t<PropertyGroup>\n");
+               string path_str;
+               for(const FS::Path &p: build_info.incpath)
+                       append(path_str, ";", p.str());
+               IO::print(out, "\t\t<NMakeIncludeSearchPath>%s</NMakeIncludeSearchPath>\n", path_str);
+               IO::print(out, "\t</PropertyGroup>\n");
+       }
+
+       IO::print(out, "\t<ItemGroup>\n");
+       for(const FileTarget *s: sources)
+               IO::print(out, "\t\t<ClCompile Include=\"%s\" />\n", s->get_path());
+       IO::print(out, "\t</ItemGroup>\n");
+
+       IO::print(out, "\t<ItemGroup>\n");
+       for(const FileTarget *i: includes)
+               IO::print(out, "\t\t<ClInclude Include=\"%s\" />\n", i->get_path());
+       IO::print(out, "\t</ItemGroup>\n");
+
+       IO::print(out, "\t<ItemGroup>\n");
+       for(const FileTarget *t: others)
+               IO::print(out, "\t\t<None Include=\"%s\" />\n", t->get_path());
+       IO::print(out, "\t</ItemGroup>\n");
+
+       IO::print(out, "\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n");
+       IO::print(out, "</Project>\n");
+
+       return true;
+}
diff --git a/plugins/builtin/vcxprojectgenerator.h b/plugins/builtin/vcxprojectgenerator.h
new file mode 100644 (file)
index 0000000..ce96a54
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef VCXPROJECTGENERATOR_H_
+#define VCXPROJECTGENERATOR_H_
+
+#include <msp/builder/tool.h>
+
+class VcxProjectFile;
+
+class VcxProjectGenerator: public Tool
+{
+public:
+       VcxProjectGenerator(Builder &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static bool _run(const VcxProjectFile &);
+};
+
+#endif
diff --git a/plugins/builtin/vssolutionfile.cpp b/plugins/builtin/vssolutionfile.cpp
new file mode 100644 (file)
index 0000000..691e1c6
--- /dev/null
@@ -0,0 +1,31 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/core/algorithm.h>
+#include "vssolutionfile.h"
+
+using namespace std;
+using namespace Msp;
+
+VsSolutionFile::VsSolutionFile(Builder &b, const SourcePackage &p):
+       FileTarget(b, p, p.get_source_directory()/(p.get_name()+".sln"))
+{
+       tool = &builder.get_toolchain().get_tool("VSSG");
+}
+
+void VsSolutionFile::find_dependencies()
+{
+       if(FileTarget *project = builder.get_vfs().get_target(package->get_source_directory()/(package->get_name()+".vcxproj")))
+               add_dependency(*project);
+
+       Package::Requirements reqs = package->get_required_packages();
+       for(auto i=reqs.begin(); i!=reqs.end(); ++i)
+               if(const SourcePackage *spkg = dynamic_cast<const SourcePackage *>(*i))
+               {
+                       if(FileTarget *project = builder.get_vfs().get_target(spkg->get_source_directory()/(spkg->get_name()+".vcxproj")))
+                               add_dependency(*project);
+
+                       for(Package *r: spkg->get_required_packages())
+                               if(!any_equals(reqs, r))
+                                       reqs.push_back(r);
+               }
+}
diff --git a/plugins/builtin/vssolutionfile.h b/plugins/builtin/vssolutionfile.h
new file mode 100644 (file)
index 0000000..402caa7
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef VSSOLUTIONFILE_H_
+#define VSSOLUTIONFILE_H_
+
+#include <msp/builder/filetarget.h>
+
+class VsSolutionFile: public FileTarget
+{
+public:
+       VsSolutionFile(Builder &, const SourcePackage &);
+
+       const char *get_type() const override { return "VsSolutionFile"; }
+protected:
+       void find_dependencies() override;
+};
+
+#endif
diff --git a/plugins/builtin/vssolutiongenerator.cpp b/plugins/builtin/vssolutiongenerator.cpp
new file mode 100644 (file)
index 0000000..60602e1
--- /dev/null
@@ -0,0 +1,68 @@
+#include <cstring>
+#include <msp/builder/builder.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include "vcxprojectfile.h"
+#include "vssolutionfile.h"
+#include "vssolutiongenerator.h"
+
+using namespace std;
+using namespace Msp;
+
+VsSolutionGenerator::VsSolutionGenerator(Builder &b):
+       Tool(b, "VSSG")
+{
+       set_run_internal(_run);
+}
+
+Target *VsSolutionGenerator::create_target(const vector<Target *> &, const string &)
+{
+       throw logic_error("Not implemented");
+}
+
+bool VsSolutionGenerator::_run(const VsSolutionFile &solution)
+{
+       const SourcePackage &spkg = *solution.get_package();
+       Builder &builder = spkg.get_builder();
+
+       IO::BufferedFile out(solution.get_path().str(), IO::M_WRITE);
+       IO::print(out, "Microsoft Visual Studio Solution File, Format Version 12.00\n");
+       IO::print(out, "MinimumVisualStudioVersion = 10.0.40219.1\n");
+
+       vector<const VcxProjectFile *> projects;
+       for(const Target *t: solution.get_dependencies())
+               if(const VcxProjectFile *project = dynamic_cast<const VcxProjectFile *>(t))
+                       projects.push_back(project);
+
+       for(const VcxProjectFile *p: projects)
+       {
+               const SourcePackage *pkg = p->get_package();
+               IO::print(out, "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"%s\", \"%s\", \"{%s}\"\nEndProject\n",
+                       pkg->get_name(), p->get_path(), p->get_guid());
+       }
+
+       vector<string> build_types = builder.get_build_types();
+       const char *platforms[] = { "x86", "x64" };
+
+       IO::print(out, "Global\n");
+       IO::print(out, "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n");
+       for(const string &t: build_types)
+               for(const char *p: platforms)
+                       IO::print(out, "\t\t%s|%s = %s|%s\n", t, p, t, p);
+       IO::print(out, "\tEndGlobalSection\n");
+       IO::print(out, "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n");
+       for(const VcxProjectFile *p: projects)
+               for(const string &t: build_types)
+                       for(const char *f: platforms)
+                       {
+                               const char *project_platform = (!strcmp(f, "x86") ? "Win32" : f);
+                               IO::print(out, "\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n", p->get_guid(), t, f, t, project_platform);
+                               if(p->get_package()==&spkg)
+                                       IO::print(out, "\t\t{%s}.%s|%s.Build.0 = %s|%s\n", p->get_guid(), t, f, t, project_platform);
+                       }
+       IO::print(out, "\tEndGlobalSection\n");
+       IO::print(out, "EndGlobal\n");
+
+       return true;
+}
diff --git a/plugins/builtin/vssolutiongenerator.h b/plugins/builtin/vssolutiongenerator.h
new file mode 100644 (file)
index 0000000..c98fcb2
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef VSSOLUTIONGENERATOR_H_
+#define VSSOLUTIONGENERATOR_H_
+
+#include <msp/builder/tool.h>
+
+class VsSolutionFile;
+
+class VsSolutionGenerator: public Tool
+{
+public:
+       VsSolutionGenerator(Builder &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static bool _run(const VsSolutionFile &);
+};
+
+#endif
diff --git a/plugins/clang/clangcompiler.cpp b/plugins/clang/clangcompiler.cpp
new file mode 100644 (file)
index 0000000..bcd73d1
--- /dev/null
@@ -0,0 +1,10 @@
+#include "clangcompiler.h"
+
+using namespace std;
+using namespace Msp;
+
+ClangCompiler::ClangCompiler(Builder &b, const Architecture &a, const string &t):
+       CustomizedTool(b, t, a)
+{
+       set_command((tag=="CXX" ? "clang++" : "clang"), true);
+}
diff --git a/plugins/clang/clangcompiler.h b/plugins/clang/clangcompiler.h
new file mode 100644 (file)
index 0000000..034b636
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef CLANGCOMPILER_H_
+#define CLANGCOMPILER_H_
+
+#include <msp/builder/customizedtool.h>
+
+class ClangCompiler: public CustomizedTool
+{
+public:
+       ClangCompiler(Builder &, const Architecture &, const std::string &);
+};
+
+#endif
diff --git a/plugins/clang/clanglinker.cpp b/plugins/clang/clanglinker.cpp
new file mode 100644 (file)
index 0000000..fb15451
--- /dev/null
@@ -0,0 +1,24 @@
+#include <msp/builder/builder.h>
+#include <msp/fs/stat.h>
+#include "clanglinker.h"
+
+using namespace Msp;
+
+ClangLinker::ClangLinker(Builder &b, const Architecture &a):
+       CustomizedTool(b, "LINK", a)
+{
+       set_command("clang", true);
+}
+
+void ClangLinker::do_prepare(ToolData &tool) const
+{
+       parent.prepare();
+       CustomizedTool::do_prepare(tool);
+       for(const FS::Path &p: parent.get_system_path())
+               if(FS::exists(p/"libstdc++.so"))
+               {
+                       builder.get_logger().log("tools", "Got %s gcc system path: %s", static_cast<const Tool &>(tool).get_tag(), p);
+                       tool.system_path.push_back(p);
+                       break;
+               }
+}
diff --git a/plugins/clang/clanglinker.h b/plugins/clang/clanglinker.h
new file mode 100644 (file)
index 0000000..a6518b1
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef CLANGLINKER_H_
+#define CLANGLINKER_H_
+
+#include <msp/builder/customizedtool.h>
+
+class ClangLinker: public CustomizedTool
+{
+public:
+       ClangLinker(Builder &, const Architecture &);
+
+protected:
+       void do_prepare(ToolData &) const override;
+};
+
+#endif
diff --git a/plugins/clang/clangtools.cpp b/plugins/clang/clangtools.cpp
new file mode 100644 (file)
index 0000000..385a279
--- /dev/null
@@ -0,0 +1,26 @@
+#include <msp/builder/architecture.h>
+#include "clangcompiler.h"
+#include "clanglinker.h"
+#include "clangtools.h"
+
+using namespace std;
+
+ClangTools::ClangTools(Builder &builder, const Architecture &arch):
+       Toolchain("clang", get_priority(arch))
+{
+       add_tool(new ClangCompiler(builder, arch, "CC"));
+       add_tool(new ClangCompiler(builder, arch, "CXX"));
+       add_tool(new ClangCompiler(builder, arch, "OBJC"));
+
+       add_tool(new ClangLinker(builder, arch));
+}
+
+int ClangTools::get_priority(const Architecture &arch)
+{
+       if(arch.get_toolchain()=="clang")
+               return 20;
+       else if(arch.get_system()=="darwin" || arch.get_system()=="freebsd")
+               return 10;
+       else
+               return 0;
+}
diff --git a/plugins/clang/clangtools.h b/plugins/clang/clangtools.h
new file mode 100644 (file)
index 0000000..971404a
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef CLANGTOOLS_H_
+#define CLANGTOOLS_H_
+
+#include <msp/builder/toolchain.h>
+
+class Architecture;
+class Builder;
+
+class ClangTools: public Toolchain
+{
+public:
+       ClangTools(Builder &, const Architecture &);
+
+       static int get_priority(const Architecture &);
+};
+
+#endif
diff --git a/plugins/datafile/datacollection.cpp b/plugins/datafile/datacollection.cpp
new file mode 100644 (file)
index 0000000..189eb61
--- /dev/null
@@ -0,0 +1,27 @@
+#include <msp/builder/component.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/fs/utils.h>
+#include "datacollection.h"
+#include "datatransform.h"
+
+using namespace Msp;
+
+DataCollection::DataCollection(Builder &b, const Component &c, DataTransform &s):
+       FileTarget(b, c.get_package(), generate_target_path(c, s.get_path())),
+       source(s)
+{
+       component = &c;
+       add_dependency(source);
+}
+
+Msp::FS::Path DataCollection::generate_target_path(const Component &comp, const Msp::FS::Path &src)
+{
+       return comp.get_package().get_temp_directory()/comp.get_name()/(FS::basepart(FS::basename(src))+".mdc");
+}
+
+void DataCollection::find_dependencies()
+{
+       source.prepare();
+       for(Target *d: source.get_transitive_dependencies())
+               add_dependency(*d);
+}
diff --git a/plugins/datafile/datacollection.h b/plugins/datafile/datacollection.h
new file mode 100644 (file)
index 0000000..2fca294
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef DATACOLLECTION_H_
+#define DATACOLLECTION_H_
+
+#include <msp/builder/filetarget.h>
+
+class DataTransform;
+
+class DataCollection: public FileTarget
+{
+private:
+       DataTransform &source;
+
+public:
+       DataCollection(Builder &, const Component &, DataTransform &);
+private:
+       static Msp::FS::Path generate_target_path(const Component &, const Msp::FS::Path &);
+
+public:
+       const char *get_type() const override { return "DataCollection"; }
+       DataTransform &get_source() const { return source; }
+
+private:
+       void find_dependencies() override;
+};
+
+#endif
diff --git a/plugins/datafile/datapack.cpp b/plugins/datafile/datapack.cpp
new file mode 100644 (file)
index 0000000..2abcd52
--- /dev/null
@@ -0,0 +1,21 @@
+#include <msp/builder/component.h>
+#include <msp/builder/sourcepackage.h>
+#include "datapack.h"
+
+using namespace std;
+
+DataPack::DataPack(Builder &b, const Component &c, const vector<FileTarget *> &f):
+       FileTarget(b, c.get_package(), generate_target_path(c)),
+       files(f)
+{
+       component = &c;
+       for(FileTarget *t: files)
+               add_dependency(*t);
+
+       install_location = Msp::FS::Path("share")/package->get_name();
+}
+
+Msp::FS::Path DataPack::generate_target_path(const Component &comp)
+{
+       return comp.get_package().get_output_directory()/(comp.get_name()+".mdp");
+}
diff --git a/plugins/datafile/datapack.h b/plugins/datafile/datapack.h
new file mode 100644 (file)
index 0000000..80a1b0d
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef DATAPACK_H_
+#define DATAPACK_H_
+
+#include <msp/builder/filetarget.h>
+
+class DataPack: public FileTarget
+{
+private:
+       std::vector<FileTarget *> files;
+
+public:
+       DataPack(Builder &, const Component &, const std::vector<FileTarget *> &);
+private:
+       static Msp::FS::Path generate_target_path(const Component &);
+
+public:
+       const char *get_type() const override { return "DataPack"; }
+
+       const std::vector<FileTarget *> &get_files() const { return files; }
+};
+
+#endif
diff --git a/plugins/datafile/datapackcomponent.cpp b/plugins/datafile/datapackcomponent.cpp
new file mode 100644 (file)
index 0000000..9b628f4
--- /dev/null
@@ -0,0 +1,41 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/builder/tool.h>
+#include <msp/fs/utils.h>
+#include "datapackcomponent.h"
+#include "datasourcefile.h"
+
+using namespace std;
+using namespace Msp;
+
+DataPackComponent::DataPackComponent(SourcePackage &p, const string &n):
+       Component(p, n)
+{ }
+
+void DataPackComponent::create_targets() const
+{
+       Builder &builder = package.get_builder();
+       Tool &dcomp = builder.get_toolchain().get_tool("DATA");
+
+       vector<Target *> files;
+       for(const FS::Path &s: collect_source_files())
+       {
+               string ext = FS::extpart(FS::basename(s));
+               if(ext==".mdt")
+               {
+                       Target *src = dcomp.create_source(*this, s);
+                       files.push_back(dcomp.create_target(*src, "collection"));
+               }
+               else if(Target *tgt = builder.get_vfs().get_target(s))
+                       files.push_back(tgt);
+               else
+                       files.push_back(new DataSourceFile(builder, *this, s));
+       }
+
+       Target *result = dcomp.create_target(files, "pack");
+
+       BuildGraph &build_graph = builder.get_build_graph();
+       build_graph.add_primary_target(*result);
+       if(install)
+               build_graph.add_installed_target(*result);
+}
diff --git a/plugins/datafile/datapackcomponent.h b/plugins/datafile/datapackcomponent.h
new file mode 100644 (file)
index 0000000..3729922
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef DATAPACKCOMPONENT_H_
+#define DATAPACKCOMPONENT_H_
+
+#include <msp/builder/component.h>
+
+class DataPackComponent: public Component
+{
+public:
+       DataPackComponent(SourcePackage &, const std::string &);
+
+       void create_targets() const override;
+};
+
+#endif
diff --git a/plugins/datafile/datasourcefile.h b/plugins/datafile/datasourcefile.h
new file mode 100644 (file)
index 0000000..8d2fcd5
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef DATASOURCEFILE_H_
+#define DATASOURCEFILE_H_
+
+#include <msp/builder/sourcefile.h>
+
+class DataSourceFile: public SourceFile
+{
+public:
+       DataSourceFile(Builder &b, const Msp::FS::Path &p): SourceFile(b, p) { }
+       DataSourceFile(Builder &b, const Component &c, const Msp::FS::Path &p): SourceFile(b, c, p) { }
+
+       const char *get_type() const override { return "DataSourceFile"; }
+};
+
+#endif
diff --git a/plugins/datafile/datatool.cpp b/plugins/datafile/datatool.cpp
new file mode 100644 (file)
index 0000000..04592e3
--- /dev/null
@@ -0,0 +1,107 @@
+#include <stdexcept>
+#include <msp/builder/builder.h>
+#include <msp/builder/component.h>
+#include <msp/builder/externaltask.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/fs/utils.h>
+#include "datacollection.h"
+#include "datapack.h"
+#include "datatool.h"
+#include "datatransform.h"
+
+using namespace std;
+using namespace Msp;
+
+DataTool::DataTool(Builder &b):
+       Tool(b, "DATA")
+{
+       set_command("mspdatatool");
+       set_run(_run);
+       input_suffixes.push_back(".mdt");
+}
+
+Target *DataTool::create_source(const Component &comp, const FS::Path &path) const
+{
+       return new DataTransform(builder, comp, path);
+}
+
+Target *DataTool::create_target(const vector<Target *> &sources, const string &arg)
+{
+       if(arg=="collection")
+       {
+               if(sources.size()!=1)
+                       throw invalid_argument("DataTool::create_target");
+               DataTransform &source = dynamic_cast<DataTransform &>(*sources.front());
+               DataCollection *coll = new DataCollection(builder, *source.get_component(), source);
+               coll->set_tool(*this);
+               return coll;
+       }
+       else if(arg=="pack")
+       {
+               if(sources.empty())
+                       throw invalid_argument("DataTool::create_target");
+               vector<FileTarget *> files;
+               files.reserve(sources.size());
+               for(Target *t: sources)
+                       files.push_back(&dynamic_cast<FileTarget &>(*t));
+               DataPack *pack = new DataPack(builder, *files.front()->get_component(), files);
+               pack->set_tool(*this);
+               return pack;
+       }
+       else
+               throw invalid_argument("DataTool::create_target");
+}
+
+string DataTool::create_build_signature(const BuildInfo &binfo) const
+{
+       string result = Tool::create_build_signature(binfo);
+       if(binfo.debug || binfo.optimize)
+               result += ',';
+       if(binfo.debug)
+               result += 'g';
+       if(binfo.optimize>0)
+       {
+               result += 'b';
+               if(binfo.optimize>1)
+                       result += 'z';
+       }
+       return result;
+}
+
+Task *DataTool::_run(const Target &tgt)
+{
+       const Tool &tool = *tgt.get_tool();
+       const Component &comp = *tgt.get_component();
+       FS::Path work_dir = comp.get_package().get_source_directory();
+
+       vector<string> argv;
+       argv.push_back(tool.get_executable()->get_path().str());
+
+       argv.push_back("-o");
+       argv.push_back(FS::relative(dynamic_cast<const FileTarget &>(tgt).get_path(), work_dir).str());
+
+       BuildInfo binfo;
+       tgt.collect_build_info(binfo);
+       if(binfo.debug)
+               argv.push_back("-g");
+       if(binfo.optimize>0)
+       {
+               argv.push_back("-b");
+               if(binfo.optimize>1)
+                       argv.push_back("-z");
+       }
+
+       if(const DataCollection *coll = dynamic_cast<const DataCollection *>(&tgt))
+       {
+               argv.push_back("-c");
+               argv.push_back(FS::relative(coll->get_source().get_path(), work_dir).str());
+       }
+       else if(const DataPack *pack = dynamic_cast<const DataPack *>(&tgt))
+       {
+               argv.push_back("-p");
+               for(const FileTarget *f: pack->get_files())
+                       argv.push_back(FS::relative(f->get_path(), work_dir).str());
+       }
+
+       return new ExternalTask(argv, work_dir);
+}
diff --git a/plugins/datafile/datatool.h b/plugins/datafile/datatool.h
new file mode 100644 (file)
index 0000000..a76f7f1
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef DATACOMPILER_H_
+#define DATACOMPILER_H_
+
+#include <msp/builder/tool.h>
+
+class DataTool: public Tool
+{
+public:
+       DataTool(Builder &);
+
+       Target *create_source(const Component &, const Msp::FS::Path &) const override;
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+       std::string create_build_signature(const BuildInfo &) const override;
+
+private:
+       static Task *_run(const Target &);
+};
+
+#endif
diff --git a/plugins/datafile/datatransform.cpp b/plugins/datafile/datatransform.cpp
new file mode 100644 (file)
index 0000000..c462ce2
--- /dev/null
@@ -0,0 +1,74 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/cache.h>
+#include <msp/builder/component.h>
+#include <msp/builder/file.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/regex.h>
+#include "datatransform.h"
+
+using namespace std;
+using namespace Msp;
+
+DataTransform::DataTransform(Builder &b, const Component &c, const FS::Path &p):
+       FileTarget(b, c.get_package(), p)
+{
+       component = &c;
+
+       if(FS::Stat st = FS::lstat(FS::dirname(path)))
+               dir_mtime = st.get_modify_time();
+}
+
+void DataTransform::find_dependencies()
+{
+       vector<string> files;
+       Cache &cache = component->get_package().get_cache();
+       const Time::TimeStamp &cache_mtime = cache.get_mtime();
+       if(mtime<cache_mtime && dir_mtime<cache_mtime && cache.has_key(this, "files"))
+               files = cache.get_values(this, "files");
+       else
+       {
+               builder.get_logger().log("files", "Reading imports from %s", path.str());
+               IO::File in(path.str());
+               DataFile::Parser parser(in, path.str());
+
+               vector<string> dir_files;
+               while(!in.eof())
+               {
+                       DataFile::Statement st = parser.parse();
+                       if(st.keyword=="for_each")
+                       {
+                               // There's bound to be at least one file: the transform itself
+                               if(dir_files.empty())
+                               {
+                                       FS::Path dir = FS::dirname(path);
+                                       builder.get_logger().log("files", "Traversing %s", dir.str());
+                                       dir_files = list_files(dir);
+                               }
+
+                               for(const DataFile::Value &a: st.args)
+                               {
+                                       Regex re(a.get<string>());
+                                       for(const string &f: dir_files)
+                                               if(re.match(f))
+                                                       files.push_back(f);
+                               }
+                       }
+                       else if(st.keyword=="file" && st.args.size()==1)
+                               files.push_back(st.args.front().get<string>());
+               }
+
+               cache.set_values(this, "files", files);
+       }
+
+       for(const string &f: files)
+       {
+               FS::Path file_path = FS::dirname(path)/f;
+               if(Target *tgt = builder.get_vfs().get_target(file_path))
+                       add_transitive_dependency(*tgt);
+               else
+                       add_transitive_dependency(*new File(builder, *package, file_path));
+       }
+}
diff --git a/plugins/datafile/datatransform.h b/plugins/datafile/datatransform.h
new file mode 100644 (file)
index 0000000..9b11e61
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef DATATRANSFORM_H_
+#define DATATRANSFORM_H_
+
+#include <msp/builder/filetarget.h>
+
+class DataTransform: public FileTarget
+{
+private:
+       Msp::Time::TimeStamp dir_mtime;
+
+public:
+       DataTransform(Builder &, const Component &, const Msp::FS::Path &);
+
+       const char *get_type() const override { return "DataTransform"; }
+
+private:
+       void find_dependencies() override;
+};
+
+#endif
diff --git a/plugins/gnu/gnuarchiver.cpp b/plugins/gnu/gnuarchiver.cpp
new file mode 100644 (file)
index 0000000..c977eaa
--- /dev/null
@@ -0,0 +1,54 @@
+#include <stdexcept>
+#include <msp/builder/builder.h>
+#include <msp/builder/component.h>
+#include <msp/builder/objectfile.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/builder/staticlibrary.h>
+#include <msp/fs/path.h>
+#include <msp/fs/utils.h>
+#include "gnuarchiver.h"
+
+using namespace std;
+using namespace Msp;
+
+GnuArchiver::GnuArchiver(Builder &b, const Architecture &a):
+       Tool(b, &a, "AR")
+{
+       set_command("ar", true);
+       input_suffixes.push_back(".o");
+       processing_unit = COMPONENT;
+       set_run_external(_run);
+}
+
+Target *GnuArchiver::create_target(const vector<Target *> &sources, const string &)
+{
+       if(sources.empty())
+               throw invalid_argument("GnuArchiver::create_target");
+
+       vector<ObjectFile *> objs;
+       objs.reserve(sources.size());
+       for(Target *s: sources)
+               objs.push_back(&dynamic_cast<ObjectFile &>(*s));
+
+       const Component &comp = *objs.front()->get_component();
+       StaticLibrary *lib = new StaticLibrary(builder, comp, objs);
+       lib->set_tool(*this);
+       return lib;
+}
+
+ExternalTask::Arguments GnuArchiver::_run(const StaticLibrary &lib, FS::Path &work_dir)
+{
+       const Tool &tool = *lib.get_tool();
+
+       vector<string> argv;
+       argv.push_back(tool.get_executable()->get_path().str());
+       argv.push_back("rc");
+
+       argv.push_back(relative(lib.get_path(), work_dir).str());
+
+       for(Target *d: lib.get_dependencies())
+               if(ObjectFile *obj = dynamic_cast<ObjectFile *>(d))
+                       argv.push_back(relative(obj->get_path(), work_dir).str());
+
+       return argv;
+}
diff --git a/plugins/gnu/gnuarchiver.h b/plugins/gnu/gnuarchiver.h
new file mode 100644 (file)
index 0000000..81ed47e
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef GNUARCHIVER_H_
+#define GNUARCHIVER_H_
+
+#include <msp/builder/tool.h>
+
+class StaticLibrary;
+
+class GnuArchiver: public Tool
+{
+public:
+       GnuArchiver(Builder &, const Architecture &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static ExternalTask::Arguments _run(const StaticLibrary &, Msp::FS::Path &);
+};
+
+#endif
diff --git a/plugins/gnu/gnucompiler.cpp b/plugins/gnu/gnucompiler.cpp
new file mode 100644 (file)
index 0000000..ba74409
--- /dev/null
@@ -0,0 +1,353 @@
+#include <msp/builder/architecture.h>
+#include <msp/builder/builder.h>
+#include <msp/builder/component.h>
+#include <msp/builder/csourcefile.h>
+#include <msp/builder/objcsourcefile.h>
+#include <msp/builder/objectfile.h>
+#include <msp/builder/sourcefile.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/core/maputils.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include "gnucompiler.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace {
+
+const char *cpus[] =
+{
+       "athlonxp", "athlon-xp",
+       "armv7a",   "armv7-a",
+       0
+};
+
+}
+
+GnuCompiler::GnuCompiler(Builder &b, const Architecture &a, const string &t):
+       Tool(b, &a, t)
+{
+       if(tag=="CC")
+       {
+               input_suffixes.push_back(".c");
+               aux_suffixes.push_back(".h");
+       }
+       else if(tag=="CXX")
+       {
+               input_suffixes.push_back(".cpp");
+               input_suffixes.push_back(".cc");
+               aux_suffixes.push_back(".hpp");
+       }
+       else if(tag=="OBJC")
+       {
+               input_suffixes.push_back(".m");
+               build_info.libs.push_back("objc");
+       }
+       else
+               throw invalid_argument("GnuCompiler::GnuCompiler");
+
+       set_command((tag=="CXX" ? "g++" : "gcc"), true);
+       set_run_external(_run);
+}
+
+Target *GnuCompiler::create_source(const Component &comp, const FS::Path &path) const
+{
+       if(tag=="OBJC")
+               return new ObjCSourceFile(builder, comp, path);
+       else
+               return new CSourceFile(builder, comp, path);
+}
+
+Target *GnuCompiler::create_source(const FS::Path &path) const
+{
+       if(tag=="OBJC")
+               return new ObjCSourceFile(builder, path);
+       else
+               return new CSourceFile(builder, path);
+}
+
+Target *GnuCompiler::create_target(const vector<Target *> &sources, const string &)
+{
+       if(sources.size()!=1)
+               throw invalid_argument("GnuCompiler::create_target");
+       SourceFile &source = dynamic_cast<SourceFile &>(*sources.front());
+       ObjectFile *obj = new ObjectFile(builder, *source.get_component(), source);
+       obj->set_tool(*this);
+       return obj;
+}
+
+string GnuCompiler::create_build_signature(const BuildInfo &binfo) const
+{
+       if(!executable)
+               return string();
+
+       string result = Tool::create_build_signature(binfo);
+       if(!architecture->get_cpu().empty())
+       {
+               result += ",m";
+               result += architecture->get_cpu();
+       }
+       if(binfo.debug || binfo.optimize)
+               result += ',';
+       if(binfo.debug)
+               result += 'g';
+       if(binfo.optimize)
+       {
+               result += 'O';
+               result += (binfo.optimize>0 ? '0'+binfo.optimize : 's');
+       }
+       return result;
+}
+
+void GnuCompiler::do_prepare(ToolData &tool) const
+{
+       tool.extra_data = 0U;
+       prepare_syspath(tool);
+       prepare_version(tool);
+
+       if(tag=="CXX")
+               tool.build_info.libs.push_back("stdc++");
+}
+
+void GnuCompiler::prepare_syspath(ToolData &tool) const
+{
+       bool path_found = false;
+       const FS::Path &sysroot = tool.build_info.sysroot;
+       const std::string &tool_tag = static_cast<Tool &>(tool).get_tag();
+
+       const FileTarget *exe = static_cast<Tool &>(tool).get_executable();
+       if(exe)
+       {
+               ExternalTask::Arguments argv;
+               argv.push_back(exe->get_path().str());
+               argv.push_back("-Wp,-v");
+               argv.push_back("-E");
+               if(tag=="CXX")
+                       argv.push_back("-xc++");
+               if(!sysroot.empty())
+                       argv.push_back("--sysroot="+sysroot.str());
+               argv.push_back("-");
+
+               builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
+               try
+               {
+                       string output = ExternalTask::run_and_capture_output(argv, FS::Path(), true);
+                       string::size_type start = 0;
+                       bool record_path = false;
+                       while(start<output.size())
+                       {
+                               string::size_type newline = output.find('\n', start);
+                               if(!output.compare(start, 34, "#include <...> search starts here:"))
+                               {
+                                       record_path = true;
+                                       path_found = true;
+                               }
+                               else if(!output.compare(start, 19, "End of search list."))
+                                       record_path = false;
+                               else if(record_path)
+                               {
+                                       FS::Path path = strip(output.substr(start, newline-start));
+                                       builder.get_logger().log("tools", "Got %s system path: %s", tool_tag, path);
+                                       tool.system_path.push_back(path);
+                               }
+                               start = newline+1;
+                       }
+               }
+               catch(const runtime_error &)
+               { }
+       }
+
+       if(!path_found)
+       {
+               builder.get_logger().log("tools", "No %s system path found, using defaults", tool_tag);
+               const Architecture &arch = *static_cast<Tool &>(tool).get_architecture();
+               if(!sysroot.empty())
+                       tool.system_path.push_back(sysroot/"usr/include");
+               else if(arch.is_native())
+                       tool.system_path.push_back("/usr/include");
+               else
+                       tool.system_path.push_back(format("/usr/%s/include", arch.get_cross_prefix()));
+       }
+}
+
+void GnuCompiler::prepare_version(ToolData &tool) const
+{
+       const FileTarget *exe = static_cast<Tool &>(tool).get_executable();
+       if(!exe)
+               return;
+
+       string exe_path = exe->get_path().str();
+       unsigned version = query_version(exe_path, "-dumpversion");
+       if(version>=0x70000)
+       {
+               unsigned v = query_version(exe_path, "-dumpfullversion");
+               if(v)
+                       version = v;
+       }
+       tool.extra_data = version;
+       builder.get_logger().log("tools", "%s version is %d.%d.%d", FS::basename(exe->get_path()), version>>16, (version>>8)&0xFF, version&0xFF);
+}
+
+unsigned GnuCompiler::query_version(const string &exe_path, const string &arg) const
+{
+       ExternalTask::Arguments argv;
+       argv.push_back(exe_path);
+       argv.push_back(arg);
+
+       builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
+       unsigned ver = 0;
+       try
+       {
+               string version_str = strip(ExternalTask::run_and_capture_output(argv));
+
+               vector<string> version_parts = split(version_str, '.');
+               for(unsigned i=0; (i<3 && i<version_parts.size()); ++i)
+                       ver |= lexical_cast<unsigned>(version_parts[i])<<(16-8*i);
+       }
+       catch(const runtime_error &)
+       { }
+
+       return ver;
+}
+
+ExternalTask::Arguments GnuCompiler::_run(const ObjectFile &object, FS::Path &work_dir)
+{
+       const Tool &tool = *object.get_tool();
+       const Architecture &arch = *tool.get_architecture();
+
+       ExternalTask::Arguments argv;
+       argv.push_back(tool.get_executable()->get_path().str());
+       argv.push_back("-c");
+
+       BuildInfo binfo;
+       object.collect_build_info(binfo);
+
+       const std::string &tool_tag = tool.get_tag();
+       string tag_for_std = (tool_tag=="OBJC" ? "CC" : tool_tag);
+       if(binfo.standards.count(tag_for_std))
+               argv.push_back("-std="+get_item(binfo.standards, tag_for_std).str());
+       if(tool_tag=="OBJC" && binfo.standards.count(tool_tag))
+               argv.push_back("-fobjc-std="+get_item(binfo.standards, tool_tag).str());
+
+       if(binfo.warning_level>=1)
+       {
+               argv.push_back("-Wall");
+               if(binfo.warning_level>=2)
+               {
+                       argv.push_back("-Wextra");
+                       argv.push_back("-Wundef");
+                       unsigned version = tool.get_extra_data();
+                       if(version>=0x80000)
+                               argv.push_back("-Wno-cast-function-type");
+               }
+               if(binfo.warning_level>=3)
+               {
+                       argv.push_back("-pedantic");
+                       argv.push_back("-Wno-long-long");
+                       argv.push_back("-Wshadow");
+                       if(tool_tag=="CC")
+                       {
+                               argv.push_back("-Wc++-compat");
+                               argv.push_back("-Wstrict-prototypes");
+                       }
+               }
+               if(binfo.warning_level>=4)
+               {
+                       // Some truly paranoid warnings
+                       argv.push_back("-Wstrict-overflow=4");
+                       argv.push_back("-Wfloat-equal");
+                       argv.push_back("-Wconversion");
+                       argv.push_back("-Wwrite-strings");
+                       argv.push_back("-Winline");
+               }
+               if(binfo.fatal_warnings)
+               {
+                       argv.push_back("-Werror");
+                       argv.push_back("-Wno-error=deprecated-declarations");
+               }
+       }
+
+       const FS::Path &sysroot = binfo.sysroot;
+       if(!sysroot.empty())
+               argv.push_back("--sysroot="+sysroot.str());
+       for(const FS::Path &p: binfo.local_incpath)
+       {
+               argv.push_back("-iquote");
+               argv.push_back(p.str());
+       }
+       for(const FS::Path &p: binfo.incpath)
+               argv.push_back("-I"+p.str());
+
+       for(const auto &kvp: binfo.defines)
+       {
+               if(kvp.second.empty())
+                       argv.push_back(format("-D%s", kvp.first));
+               else
+                       argv.push_back(format("-D%s=%s", kvp.first, kvp.second));
+       }
+
+       if(binfo.debug)
+               argv.push_back("-ggdb");
+       if(binfo.optimize)
+       {
+               if(binfo.optimize<0)
+                       argv.push_back("-Os");
+               else
+                       argv.push_back(format("-O%d", binfo.optimize));
+               if(binfo.debug)
+                       argv.push_back("-fno-omit-frame-pointer");
+       }
+       if(binfo.threads && arch.get_system()!="windows" && arch.get_system()!="darwin")
+               argv.push_back("-pthread");
+       if(object.is_used_in_shared_library() && arch.get_system()!="windows")
+               argv.push_back("-fPIC");
+
+       if((arch.get_type()=="x86" || arch.get_type()=="ppc") && !arch.is_native())
+               argv.push_back(format("-m%d", arch.get_bits()));
+
+       string cpu = arch.get_cpu();
+       if(!cpu.empty())
+       {
+               for(unsigned i=0; cpus[i]; i+=2)
+                       if(cpu==cpus[i])
+                       {
+                               cpu = cpus[i+1];
+                               break;
+                       }
+               argv.push_back("-march="+cpu);
+       }
+
+       if(!arch.get_fpu().empty())
+       {
+               if(arch.get_type()=="x86")
+               {
+                       if(arch.get_fpu()=="387")
+                               argv.push_back("-mfpmath=387");
+                       else if(!arch.get_fpu().compare(0, 3, "sse"))
+                               argv.push_back("-mfpmath=sse");
+
+                       if(arch.get_fpu()=="sse")
+                               argv.push_back("-msse2");
+                       else if(arch.get_fpu()=="sse3")
+                               argv.push_back("-msse3");
+                       else if(arch.get_fpu()=="sse4.1")
+                               argv.push_back("-msse4.1");
+               }
+               else if(arch.get_type()=="arm")
+               {
+                       argv.push_back("-mfpu="+arch.get_fpu());
+                       argv.push_back("-mfloat-abi=softfp");
+               }
+       }
+
+       FS::Path obj_path = object.get_path();
+       FS::Path src_path = object.get_source().get_path();
+
+       argv.push_back("-o");
+       argv.push_back(relative(obj_path, work_dir).str());
+       argv.push_back(relative(src_path, work_dir).str());
+
+       return argv;
+}
diff --git a/plugins/gnu/gnucompiler.h b/plugins/gnu/gnucompiler.h
new file mode 100644 (file)
index 0000000..98c71ed
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef GNUCOMPILER_H_
+#define GNUCOMPILER_H_
+
+#include <msp/builder/tool.h>
+
+class ObjectFile;
+
+/**
+Common base class for GNU compilers.  Turns SourceFiles into ObjectFiles.
+
+Since invocation is mostly the same for all language frontends, most of the
+logic is here and the individual tools only handle creating source files of
+appropriate type.
+*/
+class GnuCompiler: public Tool
+{
+public:
+       GnuCompiler(Builder &, const Architecture &, const std::string &);
+
+       Target *create_source(const Component &, const Msp::FS::Path &) const override;
+       Target *create_source(const Msp::FS::Path &) const override;
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+       std::string create_build_signature(const BuildInfo &) const override;
+protected:
+       void do_prepare(ToolData &) const override;
+       void prepare_syspath(ToolData &) const;
+       void prepare_version(ToolData &) const;
+       unsigned query_version(const std::string &, const std::string &) const;
+
+private:
+       static ExternalTask::Arguments _run(const ObjectFile &, Msp::FS::Path &);
+};
+
+#endif
diff --git a/plugins/gnu/gnulinker.cpp b/plugins/gnu/gnulinker.cpp
new file mode 100644 (file)
index 0000000..710c44b
--- /dev/null
@@ -0,0 +1,325 @@
+#include <stdexcept>
+#include <vector>
+#include <msp/builder/builder.h>
+#include <msp/builder/component.h>
+#include <msp/builder/executable.h>
+#include <msp/builder/exportdefinitions.h>
+#include <msp/builder/importlibrary.h>
+#include <msp/builder/installedfile.h>
+#include <msp/builder/objectfile.h>
+#include <msp/builder/sharedlibrary.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/builder/staticlibrary.h>
+#include <msp/core/algorithm.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include "gnucompiler.h"
+#include "gnulinker.h"
+
+using namespace std;
+using namespace Msp;
+
+GnuLinker::GnuLinker(Builder &b, const Architecture &a):
+       Tool(b, &a, "LINK")
+{
+       input_suffixes.push_back(".o");
+       input_suffixes.push_back(".a");
+
+       processing_unit = COMPONENT;
+
+       set_command("gcc", true);
+       set_run_external(_run);
+}
+
+Target *GnuLinker::create_target(const vector<Target *> &sources, const string &arg)
+{
+       if(sources.empty())
+               throw invalid_argument("GnuLinker::create_target");
+       vector<ObjectFile *> objs;
+       objs.reserve(sources.size());
+       for(Target *s: sources)
+               objs.push_back(&dynamic_cast<ObjectFile &>(*s));
+
+       const Component &comp = *objs.front()->get_component();
+       Binary *bin = 0;
+       if(arg=="shared")
+       {
+               SharedLibrary *shlib = new SharedLibrary(builder, comp, objs);
+               if(architecture->get_system()=="windows")
+               {
+                       Tool &dlltool = builder.get_toolchain().get_tool("DLL");
+                       dlltool.create_target(*shlib);
+               }
+               bin = shlib;
+       }
+       else
+               bin = new Executable(builder, comp, objs);
+       bin->set_tool(*this);
+       return bin;
+}
+
+Target *GnuLinker::create_install(Target &target) const
+{
+       if(SharedLibrary *shlib = dynamic_cast<SharedLibrary *>(&target))
+       {
+               Tool &copy = builder.get_toolchain().get_tool("CP");
+               InstalledFile *inst_tgt = dynamic_cast<InstalledFile *>(copy.create_target(target));
+               if(architecture->get_system()=="windows")
+                       builder.get_build_graph().add_installed_target(*shlib->get_import_library());
+               else
+               {
+                       string link_name = architecture->create_filename<SharedLibrary>(shlib->get_libname());
+                       if(link_name!=FS::basename(inst_tgt->get_path()))
+                               inst_tgt->set_symlink(link_name);
+               }
+               return inst_tgt;
+       }
+       else
+               return 0;
+}
+
+string GnuLinker::create_build_signature(const BuildInfo &binfo) const
+{
+       string result = Tool::create_build_signature(binfo);
+       result += ',';
+       if(binfo.libmode<=BuildInfo::STATIC)
+               result += 't';
+       else
+               result += 'd';
+       if(binfo.strip)
+               result += 's';
+       if(!binfo.libs.empty())
+       {
+               result += ",l";
+               result += join(binfo.libs.begin(), binfo.libs.end(), ",l");
+       }
+       return result;
+}
+
+void GnuLinker::do_prepare(ToolData &tool) const
+{
+       bool path_found = false;
+       const FS::Path &sysroot = tool.build_info.sysroot;
+       const std::string &tool_tag = static_cast<Tool &>(tool).get_tag();
+
+       const FileTarget *exe = static_cast<Tool &>(tool).get_executable();
+       if(exe)
+       {
+               ExternalTask::Arguments argv;
+               argv.push_back(exe->get_path().str());
+               argv.push_back("-v");
+               argv.push_back("-Wl,--verbose");
+               argv.push_back("-nostdlib");
+               if(!sysroot.empty())
+                       argv.push_back("--sysroot="+sysroot.str());
+
+               builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
+               try
+               {
+                       string output = ExternalTask::run_and_capture_output(argv, FS::Path(), true);
+
+                       string::size_type lib_path = output.find("LIBRARY_PATH=");
+                       if(lib_path!=string::npos)
+                       {
+                               string::size_type newline = output.find('\n', lib_path);
+                               for(const string &p: split(output.substr(lib_path+13, newline-lib_path-13), ':'))
+                               {
+                                       FS::Path path = strip(p);
+                                       if(!any_equals(tool.system_path, path))
+                                       {
+                                               builder.get_logger().log("tools", "Got %s frontend system path: %s", tool_tag, path);
+                                               tool.system_path.push_back(path);
+                                       }
+                                       path_found = true;
+                               }
+                       }
+
+                       string::size_type start = 0;
+                       while(start<output.size())
+                       {
+                               string::size_type search_dir = output.find("SEARCH_DIR(\"", start);
+                               if(search_dir==string::npos)
+                                       break;
+
+                               search_dir += 12;
+                               string::size_type end = output.find("\");", search_dir);
+                               if(end==string::npos)
+                                       break;
+
+                               FS::Path path;
+                               if(!output.compare(search_dir, 2, "=/"))
+                               {
+                                       search_dir += 2;
+                                       if(sysroot.empty())
+                                               path = "/";
+                                       else
+                                               path = sysroot;
+                               }
+
+                               path /= output.substr(search_dir, end-search_dir);
+                               if(!any_equals(tool.system_path, path))
+                               {
+                                       builder.get_logger().log("tools", "Got %s implicit system path: %s", tool_tag, path);
+                                       tool.system_path.push_back(path);
+                               }
+                               path_found = true;
+
+                               start = end+3;
+                       }
+               }
+               catch(...)
+               { }
+       }
+
+       if(!path_found)
+       {
+               builder.get_logger().log("tools", "No %s system path found, using defaults", tool_tag);
+               if(!sysroot.empty())
+                       tool.system_path.push_back(sysroot/"usr/lib");
+               else if(architecture->is_native())
+               {
+                       tool.system_path.push_back("/lib");
+                       tool.system_path.push_back("/usr/lib");
+                       if(architecture->match_name("pc-32-linux"))
+                       {
+                               tool.system_path.push_back("/lib/i386-linux-gnu");
+                               tool.system_path.push_back("/usr/lib/i386-linux-gnu");
+                       }
+                       else if(architecture->match_name("pc-64-linux"))
+                       {
+                               tool.system_path.push_back("/lib/x86_64-linux-gnu");
+                               tool.system_path.push_back("/usr/lib/x86_64-linux-gnu");
+                       }
+               }
+               else
+                       tool.system_path.push_back(format("/usr/%s/lib", architecture->get_cross_prefix()));
+       }
+}
+
+ExternalTask::Arguments GnuLinker::_run(const Binary &bin, FS::Path &work_dir)
+{
+       const Tool &tool = *bin.get_tool();
+       const Builder &builder = tool.get_builder();
+       const Architecture &arch = *tool.get_architecture();
+
+       ExternalTask::Arguments argv;
+       argv.push_back(tool.get_executable()->get_path().str());
+
+       if(const SharedLibrary *shlib = dynamic_cast<const SharedLibrary *>(&bin))
+       {
+               argv.push_back("-shared");
+               argv.push_back("-fPIC");
+               if(arch.get_system()!="windows" && !shlib->get_soname().empty())
+               {
+                       if(arch.get_system()=="darwin")
+                       {
+                               argv.push_back("-install_name");
+                               argv.push_back(shlib->get_soname());
+
+                               const string &ver = shlib->get_package()->get_version();
+                               const string &if_ver = shlib->get_package()->get_interface_version();
+                               if(!ver.empty() && !if_ver.empty())
+                               {
+                                       argv.push_back("-current_version");
+                                       argv.push_back(ver);
+                                       argv.push_back("-compatibility_version");
+                                       argv.push_back(if_ver);
+                               }
+                       }
+                       else
+                               argv.push_back("-Wl,-soname,"+shlib->get_soname());
+               }
+       }
+
+       BuildInfo binfo;
+       bin.collect_build_info(binfo);
+
+       const FS::Path &sysroot = binfo.sysroot;
+       if(!sysroot.empty())
+               argv.push_back("--sysroot="+sysroot.str());
+
+       FS::Path lib_dir = builder.get_prefix()/"lib";
+       if(binfo.rpath_mode==BuildInfo::ABSOLUTE)
+               argv.push_back("-Wl,-rpath,"+lib_dir.str());
+       else
+       {
+               if(binfo.rpath_mode==BuildInfo::RELATIVE)
+                       argv.push_back("-Wl,-rpath,$ORIGIN/../lib");
+               argv.push_back("-Wl,-rpath-link,"+lib_dir.str());
+       }
+
+       for(const FS::Path &p: binfo.libpath)
+               argv.push_back("-L"+p.str());
+       if(binfo.strip)
+               argv.push_back("-s");
+       if(binfo.threads && arch.get_system()!="windows" && arch.get_system()!="darwin")
+               argv.push_back("-pthread");
+
+       const Architecture &native_arch = builder.get_native_arch();
+       if(arch.is_native() && arch.get_bits()!=native_arch.get_bits())
+               argv.push_back(format("-m%d", arch.get_bits()));
+
+       argv.push_back("-o");
+       argv.push_back(relative(bin.get_path(), work_dir).str());
+
+       for(const string &s: binfo.keep_symbols)
+               argv.push_back("-u"+s);
+
+       bool static_link_ok = (binfo.libmode<=BuildInfo::STATIC);
+
+       bool has_cplusplus = false;
+       for(Target *d: bin.get_dependencies())
+       {
+               FileTarget *file = dynamic_cast<FileTarget *>(d);
+               Target *tgt = d->get_real_target();
+
+               if(ObjectFile *obj = dynamic_cast<ObjectFile *>(tgt))
+               {
+                       argv.push_back(relative(obj->get_path(), work_dir).str());
+                       if(obj->get_tool()->get_tag()=="CXX")
+                               has_cplusplus = true;
+               }
+               else if(StaticLibrary *stlib = dynamic_cast<StaticLibrary *>(tgt))
+                       argv.push_back((file?file:stlib)->get_path().str());
+               else if(SharedLibrary *shlib = dynamic_cast<SharedLibrary *>(tgt))
+               {
+                       argv.push_back("-l"+shlib->get_libname());
+                       static_link_ok = false;
+               }
+               else if(ImportLibrary *imp = dynamic_cast<ImportLibrary *>(tgt))
+               {
+                       shlib = imp->get_shared_library();
+                       if(shlib)
+                               argv.push_back("-l"+shlib->get_libname());
+                       else
+                               argv.push_back((file?file:imp)->get_path().str());
+                       static_link_ok = false;
+               }
+       }
+
+       for(const string &l: binfo.libs)
+               if(l.size()>10 && !l.compare(l.size()-10, 10, ".framework"))
+               {
+                       argv.push_back("-framework");
+                       argv.push_back(l.substr(0, l.size()-10));
+               }
+
+       if(static_link_ok)
+               argv.push_back("-static");
+       else
+       {
+               if(has_cplusplus)
+               {
+                       auto i = binfo.libmodes.find("stdc++");
+                       if(i!=binfo.libmodes.end() && i->second<=BuildInfo::STATIC)
+                               argv.push_back("-static-libstdc++");
+               }
+
+               if(arch.get_system()=="windows")
+                       argv.push_back("-Wl,--enable-auto-import");
+       }
+
+       return argv;
+}
diff --git a/plugins/gnu/gnulinker.h b/plugins/gnu/gnulinker.h
new file mode 100644 (file)
index 0000000..11a0fb1
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef GNULINKER_H_
+#define GNULINKER_H_
+
+#include <msp/builder/tool.h>
+
+class Binary;
+
+/**
+The GNU linker.  Turns ObjectFiles into Executables and SharedLibraries.  To
+create a shared library, specify "shared" as the second argument to
+create_target.
+
+Uses either gcc or g++ depending on what was used to compile the object files.
+*/
+class GnuLinker: public Tool
+{
+public:
+       GnuLinker(Builder &, const Architecture &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+       Target *create_install(Target &) const override;
+       std::string create_build_signature(const BuildInfo &) const override;
+protected:
+       void do_prepare(ToolData &) const override;
+private:
+       static ExternalTask::Arguments _run(const Binary &, Msp::FS::Path &);
+};
+
+#endif
diff --git a/plugins/gnu/gnutools.cpp b/plugins/gnu/gnutools.cpp
new file mode 100644 (file)
index 0000000..14c9f11
--- /dev/null
@@ -0,0 +1,30 @@
+#include <msp/builder/architecture.h>
+#include "gnuarchiver.h"
+#include "gnucompiler.h"
+#include "gnulinker.h"
+#include "gnutools.h"
+#include "mingwdlltool.h"
+
+GnuTools::GnuTools(Builder &builder, const Architecture &arch):
+       Toolchain("gnu", get_priority(arch))
+{
+       add_tool(new GnuCompiler(builder, arch, "CC"));
+       add_tool(new GnuCompiler(builder, arch, "CXX"));
+       add_tool(new GnuCompiler(builder, arch, "OBJC"));
+
+       add_tool(new GnuLinker(builder, arch));
+       add_tool(new GnuArchiver(builder, arch));
+
+       if(arch.get_system()=="windows")
+               add_tool(new MingwDllTool(builder, arch));
+}
+
+int GnuTools::get_priority(const Architecture &arch)
+{
+       if(arch.get_toolchain()=="gnu")
+               return 20;
+       else if(arch.get_system()=="linux")
+               return 10;
+       else
+               return 0;
+}
diff --git a/plugins/gnu/gnutools.h b/plugins/gnu/gnutools.h
new file mode 100644 (file)
index 0000000..59e2908
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef GNUTOOLS_H_
+#define GNUTOOLS_H_
+
+#include <msp/builder/toolchain.h>
+
+class Architecture;
+class Builder;
+
+class GnuTools: public Toolchain
+{
+public:
+       GnuTools(Builder &, const Architecture &);
+
+       static int get_priority(const Architecture &);
+};
+
+#endif
diff --git a/plugins/gnu/mingwdlltool.cpp b/plugins/gnu/mingwdlltool.cpp
new file mode 100644 (file)
index 0000000..c5ffdf5
--- /dev/null
@@ -0,0 +1,118 @@
+#include <cstdlib>
+#include <msp/builder/builder.h>
+#include <msp/builder/component.h>
+#include <msp/builder/exportdefinitions.h>
+#include <msp/builder/externaltask.h>
+#include <msp/builder/importlibrary.h>
+#include <msp/builder/installedfile.h>
+#include <msp/builder/objectfile.h>
+#include <msp/builder/sharedlibrary.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include "mingwdlltool.h"
+
+using namespace std;
+using namespace Msp;
+
+MingwDllTool::MingwDllTool(Builder &b, const Architecture &a):
+       Tool(b, &a, "DLL")
+{
+       set_command("dlltool", true);
+       set_run(_run);
+}
+
+Target *MingwDllTool::create_target(const vector<Target *> &sources, const string &)
+{
+       if(sources.size()!=1)
+               throw invalid_argument("MingwDllTool::create_target");
+       SharedLibrary &shlib = dynamic_cast<SharedLibrary &>(*sources.front());
+
+       vector<ObjectFile *> objs;
+       objs.reserve(shlib.get_dependencies().size());
+       for(Target *d: shlib.get_dependencies())
+               if(ObjectFile *obj = dynamic_cast<ObjectFile *>(d))
+                       objs.push_back(obj);
+
+       ExportDefinitions *exp = new ExportDefinitions(builder, *shlib.get_component(), objs);
+       exp->set_tool(*this);
+
+       ImportLibrary *imp = new ImportLibrary(builder, *shlib.get_component(), shlib, *exp);
+       imp->set_tool(*this);
+
+       return imp;
+}
+
+Target *MingwDllTool::create_install(Target &target) const
+{
+       if(ImportLibrary *imp = dynamic_cast<ImportLibrary *>(&target))
+       {
+               Tool &copy = builder.get_toolchain().get_tool("CP");
+               InstalledFile *inst_tgt = dynamic_cast<InstalledFile *>(copy.create_target(target));
+               string link_name = format("lib%s.dll.a", imp->get_shared_library()->get_libname());
+               if(link_name!=FS::basename(inst_tgt->get_path()))
+                       inst_tgt->set_symlink(link_name);
+               return inst_tgt;
+       }
+       else
+               return 0;
+}
+
+Task *MingwDllTool::_run(const Target &target)
+{
+       const Tool &tool = *target.get_tool();
+
+       const ImportLibrary *imp = dynamic_cast<const ImportLibrary *>(&target);
+       const ExportDefinitions *exp = 0;
+       if(imp)
+               exp = &dynamic_cast<const ExportDefinitions &>(*imp->get_dependencies().front());
+       else
+               exp = dynamic_cast<const ExportDefinitions *>(&target);
+       if(!imp && !exp)
+               throw invalid_argument("MingwDllTool::run");
+
+       vector<string> argv;
+       argv.push_back(tool.get_executable()->get_path().str());
+
+       /* dlltool is stupid and puts temporary files in the working directory by
+       default */
+       argv.push_back("--temp-prefix");
+       char random[8];
+       for(unsigned i=0; i<8; ++i)
+               random[i] = 'a'+(rand()%26);
+       argv.push_back(string("/tmp/")+string(random, 8));
+
+       const Component &comp = *target.get_component();
+       FS::Path work_dir = comp.get_package().get_source_directory();
+
+       if(imp)
+       {
+               const SharedLibrary &shlib = *imp->get_shared_library();
+
+               argv.push_back("-d");
+               argv.push_back(relative(exp->get_path(), work_dir).str());
+
+               argv.push_back("-D");
+               if(shlib.get_install_filename().empty())
+                       argv.push_back(FS::basename(shlib.get_path()));
+               else
+                       argv.push_back(shlib.get_install_filename());
+
+               argv.push_back("-l");
+               argv.push_back(relative(imp->get_path(), work_dir).str());
+       }
+       else
+       {
+               for(Target *d: exp->get_dependencies())
+                       if(ObjectFile *obj = dynamic_cast<ObjectFile *>(d))
+                               argv.push_back(relative(obj->get_path(), work_dir).str());
+
+               // XXX Should use dllexport, but that has some other problems to solve
+               argv.push_back("--export-all-symbols");
+
+               argv.push_back("-z");
+               argv.push_back(relative(exp->get_path(), work_dir).str());
+       }
+
+       return new ExternalTask(argv, work_dir);
+}
diff --git a/plugins/gnu/mingwdlltool.h b/plugins/gnu/mingwdlltool.h
new file mode 100644 (file)
index 0000000..d35fa19
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef DLLTOOL_H_
+#define DLLTOOL_H_
+
+#include <msp/builder/tool.h>
+
+class MingwDllTool: public Tool
+{
+public:
+       MingwDllTool(Builder &, const Architecture &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+       Target *create_install(Target &) const override;
+
+private:
+       static Task *_run(const Target &);
+};
+
+#endif
diff --git a/plugins/msvc/microsofttools.cpp b/plugins/msvc/microsofttools.cpp
new file mode 100644 (file)
index 0000000..af435bb
--- /dev/null
@@ -0,0 +1,99 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/externaltask.h>
+#include <msp/builder/logger.h>
+#include <msp/builder/sysutils.h>
+#include <msp/core/algorithm.h>
+#include <msp/fs/dir.h>
+#include <msp/strings/utils.h>
+#include "microsofttools.h"
+#include "msvcarchiver.h"
+#include "msvccompiler.h"
+#include "msvclinker.h"
+
+using namespace std;
+using namespace Msp;
+
+MicrosoftTools::MicrosoftTools(Builder &builder, const Architecture &arch):
+       Toolchain("msvc", get_priority(arch))
+{
+       find_vc_bin_dir(builder, arch);
+       find_windows_sdk_dir(builder);
+
+       add_tool(new MsvcCompiler(builder, arch, "CC", *this));
+       add_tool(new MsvcCompiler(builder, arch, "CXX", *this));
+       add_tool(new MsvcLinker(builder, arch, *this));
+       add_tool(new MsvcArchiver(builder, arch, *this));
+}
+
+void MicrosoftTools::find_vc_bin_dir(Builder &builder, const Architecture &arch)
+{
+       FS::Path program_files_x86 = get_program_files_x86_dir();
+
+       ExternalTask::Arguments argv;
+       argv.push_back((program_files_x86/"Microsoft Visual Studio"/"Installer"/"vswhere.exe").str());
+       argv.push_back("-latest");
+       argv.push_back("-property");
+       argv.push_back("installationPath");
+
+       builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
+
+       string output = ExternalTask::run_and_capture_output(argv, FS::Path(), true);
+       FS::Path vs_path = strip(output);
+
+       builder.get_logger().log("tools", "Visual Studio found in %s", vs_path);
+
+       FS::Path vc_aux_build_dir = vs_path/"VC"/"Auxiliary"/"Build";
+       builder.get_logger().log("files", "Traversing %s", vc_aux_build_dir);
+       vector<string> vc_version_files = FS::list_filtered(vc_aux_build_dir, "^Microsoft\\.VCToolsVersion\\.");
+       if(vc_version_files.empty())
+       {
+               builder.get_logger().log("problems", "MSVC tools version not found");
+               return;
+       }
+
+       sort(vc_version_files);
+       FS::Path vc_version_fn = vc_aux_build_dir/vc_version_files.back();
+       builder.get_logger().log("files", "Reading %s", vc_version_fn);
+       char buffer[256];
+       unsigned len = IO::File(vc_version_fn.str()).read(buffer, sizeof(buffer));
+       string vc_version = strip(string(buffer, len));
+
+       builder.get_logger().log("tools", "Detected MSVC version %s", vc_version);
+
+       const Architecture &native_arch = builder.get_native_arch();
+       string host = (native_arch.get_bits()==64 ? "Hostx64" : "Hostx86");
+       string target = (arch.get_bits()==64 ? "x64" : "x86");
+
+       vc_base_dir = vs_path/"VC"/"Tools"/"MSVC"/vc_version;
+       vc_bin_dir = vc_base_dir/"bin"/host/target;
+}
+
+void MicrosoftTools::find_windows_sdk_dir(Builder &builder)
+{
+       win_sdk_dir = get_registry_value<string>("HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0\\InstallationFolder");
+       if(win_sdk_dir.empty())
+               win_sdk_dir = get_program_files_x86_dir()/"Windows Kits"/"10";
+
+       builder.get_logger().log("files", "Traversing %s", win_sdk_dir/"include");
+       vector<string> sdk_versions = FS::list_filtered(win_sdk_dir/"include", "^10\\.");
+       if(sdk_versions.empty())
+       {
+               builder.get_logger().log("problems", "No Windows SDK versions found");
+               return;
+       }
+
+       sort(sdk_versions);
+       win_sdk_version = sdk_versions.back();
+
+       builder.get_logger().log("tools", "Windows SDK version %s found in %s", win_sdk_version, win_sdk_dir);
+}
+
+int MicrosoftTools::get_priority(const Architecture &arch)
+{
+       if(arch.get_toolchain()=="msvc")
+               return 20;
+       else if(arch.get_system()=="windows")
+               return 10;
+       else
+               return 0;
+}
diff --git a/plugins/msvc/microsofttools.h b/plugins/msvc/microsofttools.h
new file mode 100644 (file)
index 0000000..900bb68
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef MICROSOFTTOOLS_H_
+#define MICROSOFTTOOLS_H_
+
+#include <msp/builder/toolchain.h>
+#include <msp/fs/path.h>
+
+class Architecture;
+class Builder;
+
+class MicrosoftTools: public Toolchain
+{
+private:
+       Msp::FS::Path vc_base_dir;
+       Msp::FS::Path vc_bin_dir;
+       Msp::FS::Path win_sdk_dir;
+       std::string win_sdk_version;
+
+public:
+       MicrosoftTools(Builder &, const Architecture &);
+
+private:
+       void find_vc_bin_dir(Builder &, const Architecture &);
+       void find_windows_sdk_dir(Builder &);
+
+public:
+       const Msp::FS::Path &get_vc_base_dir() const { return vc_base_dir; }
+       const Msp::FS::Path &get_vc_bin_dir() const { return vc_bin_dir; }
+       const Msp::FS::Path &get_windows_sdk_dir() const { return win_sdk_dir; }
+       const std::string &get_windows_sdk_version() const { return win_sdk_version; }
+
+       static int get_priority(const Architecture &);
+};
+
+#endif
diff --git a/plugins/msvc/msvcarchiver.cpp b/plugins/msvc/msvcarchiver.cpp
new file mode 100644 (file)
index 0000000..adcb8e7
--- /dev/null
@@ -0,0 +1,53 @@
+#include <msp/builder/component.h>
+#include <msp/builder/objectfile.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/builder/staticlibrary.h>
+#include <msp/fs/utils.h>
+#include "microsofttools.h"
+#include "msvcarchiver.h"
+
+using namespace std;
+using namespace Msp;
+
+MsvcArchiver::MsvcArchiver(Builder &b, const Architecture &a, const MicrosoftTools &m):
+       Tool(b, &a, "AR"),
+       ms_tools(m)
+{
+       input_suffixes.push_back(".o");
+       processing_unit = COMPONENT;
+       set_command((ms_tools.get_vc_bin_dir()/"lib.exe").str(), false);
+       set_run_external(_run);
+}
+
+Target *MsvcArchiver::create_target(const vector<Target *> &sources, const string &)
+{
+       if(sources.empty())
+               throw invalid_argument("MsvcArchiver::create_target");
+
+       vector<ObjectFile *> objs;
+       objs.reserve(sources.size());
+       for(Target *s: sources)
+               objs.push_back(&dynamic_cast<ObjectFile &>(*s));
+
+       const Component &comp = *objs.front()->get_component();
+       StaticLibrary *lib = new StaticLibrary(builder, comp, objs);
+       lib->set_tool(*this);
+       return lib;
+}
+
+ExternalTask::Arguments MsvcArchiver::_run(const StaticLibrary &lib, FS::Path &work_dir)
+{
+       const Tool &tool = *lib.get_tool();
+
+       vector<string> argv;
+       argv.push_back(tool.get_executable()->get_path().str());
+       argv.push_back("/NOLOGO");
+
+       argv.push_back("/OUT:"+relative(lib.get_path(), work_dir).str());
+
+       for(Target *d: lib.get_dependencies())
+               if(ObjectFile *obj = dynamic_cast<ObjectFile *>(d))
+                       argv.push_back(relative(obj->get_path(), work_dir).str());
+
+       return argv;
+}
diff --git a/plugins/msvc/msvcarchiver.h b/plugins/msvc/msvcarchiver.h
new file mode 100644 (file)
index 0000000..89d24c6
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef MSVCARCHIVER_H_
+#define MSVCARCHIVER_H_
+
+#include <msp/builder/tool.h>
+
+class MicrosoftTools;
+class StaticLibrary;
+
+class MsvcArchiver: public Tool
+{
+private:
+       const MicrosoftTools &ms_tools;
+
+public:
+       MsvcArchiver(Builder &, const Architecture &, const MicrosoftTools &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static ExternalTask::Arguments _run(const StaticLibrary &, Msp::FS::Path &);
+};
+
+#endif
diff --git a/plugins/msvc/msvccompiler.cpp b/plugins/msvc/msvccompiler.cpp
new file mode 100644 (file)
index 0000000..2e63335
--- /dev/null
@@ -0,0 +1,181 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/component.h>
+#include <msp/builder/csourcefile.h>
+#include <msp/builder/objectfile.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/core/environ.h>
+#include <msp/core/maputils.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include "microsofttools.h"
+#include "msvccompiler.h"
+
+using namespace std;
+using namespace Msp;
+
+MsvcCompiler::MsvcCompiler(Builder &b, const Architecture &a, const string &t, const MicrosoftTools &m):
+       Tool(b, &a, t),
+       ms_tools(m)
+{
+       if(tag=="CC")
+       {
+               input_suffixes.push_back(".c");
+               aux_suffixes.push_back(".h");
+       }
+       else if(tag=="CXX")
+       {
+               input_suffixes.push_back(".cpp");
+               input_suffixes.push_back(".cc");
+               aux_suffixes.push_back(".hpp");
+       }
+       else
+               throw invalid_argument("MsvcCompiler::MsvcCompiler");
+
+       set_command((ms_tools.get_vc_bin_dir()/"cl.exe").str(), false);
+       set_run_external(_run);
+}
+
+Target *MsvcCompiler::create_source(const Component &comp, const FS::Path &path) const
+{
+       return new CSourceFile(builder, comp, path);
+}
+
+Target *MsvcCompiler::create_source(const FS::Path &path) const
+{
+       return new CSourceFile(builder, path);
+}
+
+Target *MsvcCompiler::create_target(const vector<Target *> &sources, const string &)
+{
+       if(sources.size()!=1)
+               throw invalid_argument("MsvcCompiler::create_target");
+       SourceFile &source = dynamic_cast<SourceFile &>(*sources.front());
+       ObjectFile *obj = new ObjectFile(builder, *source.get_component(), source);
+       obj->set_tool(*this);
+       return obj;
+}
+
+string MsvcCompiler::create_build_signature(const BuildInfo &binfo) const
+{
+       string result = Tool::create_build_signature(binfo);
+       result += ',';
+       if(binfo.debug)
+               result += 'g';
+       if(binfo.optimize)
+       {
+               result += 'O';
+               result += (binfo.optimize<0 ? '1' : binfo.optimize==1 ? 'x' : '2');
+       }
+       if(binfo.libmode<=BuildInfo::STATIC)
+               result += 't';
+       return result;
+}
+
+void MsvcCompiler::do_prepare(ToolData &tool) const
+{
+       const std::string &tool_tag = static_cast<Tool &>(tool).get_tag();
+
+       const FS::Path &vc_base_dir = ms_tools.get_vc_base_dir();
+       tool.system_path.push_back(vc_base_dir/"include");
+
+       const FS::Path &win_sdk_dir = ms_tools.get_windows_sdk_dir();
+       const string &win_sdk_ver = ms_tools.get_windows_sdk_version();
+       tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"ucrt");
+       tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"shared");
+       tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"um");
+
+       string path;
+       for(const FS::Path &p: tool.system_path)
+       {
+               append(path, ";", p.str());
+               builder.get_logger().log("tools", "Got %s system path: %s", tool_tag, p);
+       }
+
+       setenv("INCLUDE", path);
+}
+
+ExternalTask::Arguments MsvcCompiler::_run(const ObjectFile &object, FS::Path &work_dir)
+{
+       const Tool &tool = *object.get_tool();
+
+       ExternalTask::Arguments argv;
+       argv.push_back(tool.get_executable()->get_path().str());
+       argv.push_back("/nologo");
+       argv.push_back("/c");
+
+       BuildInfo binfo;
+       object.collect_build_info(binfo);
+
+       if(binfo.standards.count(tool.get_tag()))
+       {
+               const BuildInfo::LanguageStandard &std = get_item(binfo.standards, tool.get_tag());
+               if((tool.get_tag()=="CXX" && std.year>2011) || (tool.get_tag()=="CC" && std.year>1999))
+                       argv.push_back("/std:"+std.str());
+       }
+
+       if(binfo.warning_level>=1)
+       {
+               argv.push_back(format("/W%d", binfo.warning_level));
+               if(binfo.fatal_warnings)
+                       argv.push_back("/WX");
+               if(binfo.warning_level>=3)
+                       argv.push_back("/permissive-");
+
+               argv.push_back("/wd4068");  // Unknown pragma
+               if(binfo.warning_level<4)
+               {
+                       argv.push_back("/wd4244");  // Narrowing conversion on arg or return
+                       argv.push_back("/wd4267");  // Narrowing conversion
+               }
+       }
+       else
+               argv.push_back("/w");
+
+       for(const FS::Path &p: binfo.local_incpath)
+       {
+               argv.push_back("/I");
+               argv.push_back(p.str());
+       }
+       for(const FS::Path &p: binfo.incpath)
+       {
+               argv.push_back("/I");
+               argv.push_back(p.str());
+       }
+
+       for(const auto &kvp: binfo.defines)
+       {
+               argv.push_back("/D");
+               if(kvp.second.empty())
+                       argv.push_back(kvp.first);
+               else
+                       argv.push_back(format("%s=%s", kvp.first, kvp.second));
+       }
+
+       if(binfo.debug)
+               argv.push_back("/Z7");
+       if(binfo.optimize)
+       {
+               if(binfo.optimize<0)
+                       argv.push_back("/O1");
+               else if(binfo.optimize==1)
+                       argv.push_back("/Ox");
+               else
+                       argv.push_back("/O2");
+       }
+
+       if(binfo.libmode<=BuildInfo::STATIC)
+               argv.push_back(binfo.debug ? "/MTd" : "/MT");
+       else
+               argv.push_back(binfo.debug ? "/MDd" : "/MD");
+
+       argv.push_back("/EHsc");
+
+       FS::Path obj_path = object.get_path();
+       FS::Path src_path = object.get_source().get_path();
+
+       argv.push_back("/Fo"+relative(obj_path, work_dir).str());
+       argv.push_back(relative(src_path, work_dir).str());
+
+       return argv;
+}
diff --git a/plugins/msvc/msvccompiler.h b/plugins/msvc/msvccompiler.h
new file mode 100644 (file)
index 0000000..ef60e2e
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef MSVCCOMPILER_H_
+#define MSVCCOMPILER_H_
+
+#include <msp/builder/tool.h>
+
+class MicrosoftTools;
+class ObjectFile;
+
+class MsvcCompiler: public Tool
+{
+private:
+       const MicrosoftTools &ms_tools;
+
+public:
+       MsvcCompiler(Builder &, const Architecture &, const std::string &, const MicrosoftTools &);
+
+       Target *create_source(const Component &, const Msp::FS::Path &) const override;
+       Target *create_source(const Msp::FS::Path &) const override;
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+       std::string create_build_signature(const BuildInfo &) const override;
+
+protected:
+       void do_prepare(ToolData &) const override;
+
+public:
+       static ExternalTask::Arguments _run(const ObjectFile &, Msp::FS::Path &);
+};
+
+#endif
diff --git a/plugins/msvc/msvclinker.cpp b/plugins/msvc/msvclinker.cpp
new file mode 100644 (file)
index 0000000..8008791
--- /dev/null
@@ -0,0 +1,128 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/component.h>
+#include <msp/builder/executable.h>
+#include <msp/builder/importlibrary.h>
+#include <msp/builder/objectfile.h>
+#include <msp/builder/sharedlibrary.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/builder/staticlibrary.h>
+#include <msp/core/environ.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/utils.h>
+#include "microsofttools.h"
+#include "msvclinker.h"
+
+using namespace std;
+using namespace Msp;
+
+MsvcLinker::MsvcLinker(Builder &b, const Architecture &a, const MicrosoftTools &m):
+       Tool(b, &a, "LINK"),
+       ms_tools(m)
+{
+       input_suffixes.push_back(".o");
+       input_suffixes.push_back(".a");
+
+       processing_unit = COMPONENT;
+
+       set_command((ms_tools.get_vc_bin_dir()/"link.exe").str(), false);
+       set_run_external(_run);
+}
+
+Target *MsvcLinker::create_target(const vector<Target *> &sources, const string &arg)
+{
+       if(sources.empty())
+               throw invalid_argument("MsvcLinker::create_target");
+
+       vector<ObjectFile *> objs;
+       objs.reserve(sources.size());
+       for(Target *s: sources)
+               objs.push_back(&dynamic_cast<ObjectFile &>(*s));
+
+       const Component &comp = *objs.front()->get_component();
+       Binary *bin = 0;
+       if(arg=="shared")
+               bin = new SharedLibrary(builder, comp, objs);
+       else
+               bin = new Executable(builder, comp, objs);
+       bin->set_tool(*this);
+       return bin;
+}
+
+string MsvcLinker::create_build_signature(const BuildInfo &binfo) const
+{
+       string result = Tool::create_build_signature(binfo);
+       result += ',';
+       if(binfo.strip)
+               result += 's';
+       if(!binfo.libs.empty())
+       {
+               result += ",l";
+               result += join(binfo.libs.begin(), binfo.libs.end(), ",l");
+       }
+       return result;
+}
+
+void MsvcLinker::do_prepare(ToolData &tool) const
+{
+       const std::string &tool_tag = static_cast<Tool &>(tool).get_tag();
+       const Architecture &arch = *static_cast<Tool &>(tool).get_architecture();
+       string arch_dir = (arch.get_bits()==64 ? "x64" : "x86");
+
+       const FS::Path &vc_base_dir = ms_tools.get_vc_base_dir();
+       tool.system_path.push_back(vc_base_dir/"lib"/arch_dir);
+
+       const FS::Path &win_sdk_dir = ms_tools.get_windows_sdk_dir();
+       const string &win_sdk_ver = ms_tools.get_windows_sdk_version();
+       tool.system_path.push_back(win_sdk_dir/"lib"/win_sdk_ver/"ucrt"/arch_dir);
+       tool.system_path.push_back(win_sdk_dir/"lib"/win_sdk_ver/"um"/arch_dir);
+
+       string path;
+       for(const FS::Path &p: tool.system_path)
+       {
+               append(path, ";", p.str());
+               builder.get_logger().log("tools", "Got %s system path: %s", tool_tag, p);
+       }
+
+       setenv("LIB", path);
+}
+
+ExternalTask::Arguments MsvcLinker::_run(const Binary &bin, FS::Path &work_dir)
+{
+       const Tool &tool = *bin.get_tool();
+
+       vector<string> argv;
+       argv.push_back(tool.get_executable()->get_path().str());
+       argv.push_back("/NOLOGO");
+
+       if(dynamic_cast<const SharedLibrary *>(&bin))
+               argv.push_back("/DLL");
+
+       BuildInfo binfo;
+       bin.collect_build_info(binfo);
+
+       /*for(const FS::Path &p: binfo.libpath)
+               argv.push_back("/LIBPATH:"+p.str());*/
+       if(binfo.strip)
+               argv.push_back("/INCREMENTAL:NO");
+       else
+               argv.push_back("/DEBUG:FULL");
+
+       argv.push_back("/OUT:"+relative(bin.get_path(), work_dir).str());
+
+       for(Target *d: bin.get_dependencies())
+       {
+               FileTarget *file = dynamic_cast<FileTarget *>(d);
+               Target *tgt = d->get_real_target();
+
+               if(ObjectFile *obj = dynamic_cast<ObjectFile *>(tgt))
+                       argv.push_back(relative(obj->get_path(), work_dir).str());
+               else if(StaticLibrary *stlib = dynamic_cast<StaticLibrary *>(tgt))
+                       argv.push_back((file?file:stlib)->get_path().str());
+               else if(ImportLibrary *imp = dynamic_cast<ImportLibrary *>(tgt))
+                       argv.push_back((file?file:imp)->get_path().str());
+       }
+
+       argv.push_back("/SUBSYSTEM:CONSOLE");
+
+       return argv;
+}
diff --git a/plugins/msvc/msvclinker.h b/plugins/msvc/msvclinker.h
new file mode 100644 (file)
index 0000000..fe98c7d
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef MSVCLINKER_H_
+#define MSVCLINKER_H_
+
+#include <msp/builder/tool.h>
+
+class Binary;
+class MicrosoftTools;
+
+class MsvcLinker: public Tool
+{
+private:
+       const MicrosoftTools &ms_tools;
+
+public:
+       MsvcLinker(Builder &, const Architecture &, const MicrosoftTools &);
+
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+       std::string create_build_signature(const BuildInfo &) const override;
+
+protected:
+       void do_prepare(ToolData &data) const override;
+
+public:
+       static ExternalTask::Arguments _run(const Binary &, Msp::FS::Path &);
+};
+
+#endif
diff --git a/source/analyzer.cpp b/source/analyzer.cpp
deleted file mode 100644 (file)
index f91c298..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/fs/utils.h>
-#include <msp/io/print.h>
-#include "analyzer.h"
-#include "builder.h"
-#include "buildgraph.h"
-#include "objectfile.h"
-#include "sourcefile.h"
-#include "sourcepackage.h"
-#include "target.h"
-#include "tool.h"
-
-using namespace std;
-using namespace Msp;
-
-void Analyzer::analyze()
-{
-       if(mode==RDEPS)
-       {
-               rdepends.clear();
-               for(const auto &kvp: builder.get_build_graph().get_targets())
-               {
-                       for(Target *d: kvp.second->get_dependencies())
-                               rdepends[d].insert(kvp.second);
-                       for(Target *d: kvp.second->get_transitive_dependencies())
-                               rdepends[d].insert(kvp.second);
-               }
-       }
-
-       table.clear();
-
-       TableRow row;
-       row.push_back("Name");
-       row.push_back("Package");
-       row.push_back("Type");
-       row.push_back("Tool");
-       row.push_back("Rebuild");
-       table.push_back(row);
-       
-       Target &goals = builder.get_build_graph().get_goals();
-       if(mode==RDEPS)
-       {
-               for(Target *d: goals.get_dependencies())
-                       build_depend_table(*d, 0);
-       }
-       else
-               build_depend_table(goals, 0);
-
-       print_table();
-}
-
-void Analyzer::build_depend_table(Target &tgt, unsigned depth)
-{
-       Target *real = tgt.get_real_target();
-       if(mode==DEPS)
-       {
-               // Skip trivial targets
-               if(real!=&tgt)
-                       return build_depend_table(*real, depth);
-               if(const ObjectFile *obj = dynamic_cast<const ObjectFile *>(&tgt))
-                       return build_depend_table(obj->get_source(), depth);
-       }
-       else if(mode==REBUILD && !tgt.needs_rebuild())
-               /* All targets that depend on to-be-built targets will be rebuilt
-               themselves, so we can stop here. */
-               return;
-       
-       TableRow row;
-
-       string name;
-       const FileTarget *ft = dynamic_cast<const FileTarget *>(&tgt);
-       if(full_paths && ft)
-               name = ft->get_path().str();
-       else
-               name = tgt.get_name();
-       row.push_back(string(depth*2, ' ')+name);
-
-       const Package *pkg = tgt.get_package();
-       if(pkg)
-               row.push_back(pkg->get_name());
-       else
-               row.push_back("");
-       
-       row.push_back(tgt.get_type());
-       const Tool *tool = tgt.get_tool();
-       if(tool)
-               row.push_back(tool->get_tag());
-       else
-               row.push_back("");
-
-       if(tgt.needs_rebuild())
-               row.push_back(tgt.get_rebuild_reason());
-
-       table.push_back(row);
-
-       if(!max_depth || depth<max_depth-1)
-       {
-               Target::Dependencies depends;
-               if(mode==RDEPS)
-               {
-                       const set<Target *> &rdeps = rdepends[&tgt];
-                       depends.assign(rdeps.begin(), rdeps.end());
-               }
-               else
-               {
-                       depends = tgt.get_dependencies();
-                       const Target::Dependencies &tdeps = tgt.get_transitive_dependencies();
-                       depends.insert(depends.end(), tdeps.begin(), tdeps.end());
-               }
-
-               sort(depends, (full_paths ? target_order_full : target_order));
-
-               for(Target *d: depends)
-                       build_depend_table(*d, depth+1);
-       }
-}
-
-void Analyzer::print_table() const
-{
-       vector<string::size_type> col_width;
-
-       // Determine column widths
-       for(const vector<string> &r: table)
-       {
-               if(col_width.size()<r.size())
-                       col_width.resize(r.size(), 0);
-               for(unsigned j=0; j<r.size(); ++j)
-                       col_width[j] = max(col_width[j], r[j].size());
-       }
-
-       for(const vector<string> &r: table)
-       {
-               string line;
-               for(unsigned j=0; j<r.size(); ++j)
-               {
-                       if(j>0)
-                               line += "  ";
-                       line += lexical_cast<string>(r[j], Fmt("%-s").width(col_width[j]));
-               }
-               IO::print("%s\n", line);
-       }
-}
-
-bool Analyzer::target_order(const Target *t1, const Target *t2)
-{
-       return t1->get_name()<t2->get_name();
-}
-
-bool Analyzer::target_order_full(const Target *t1, const Target *t2)
-{
-       const FileTarget *ft1 = dynamic_cast<const FileTarget *>(t1);
-       const FileTarget *ft2 = dynamic_cast<const FileTarget *>(t2);
-       if(!ft1)
-       {
-               if(ft2)
-                       return true;
-               return target_order(t1, t2);
-       }
-       else if(!ft2)
-               return false;
-       return ft1->get_path().str()<ft2->get_path().str();
-}
diff --git a/source/analyzer.h b/source/analyzer.h
deleted file mode 100644 (file)
index d30de04..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef ANALYZER_H_
-#define ANALYZER_H_
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-class Builder;
-class Target;
-
-/**
-Performs various kinds of dependency analysis on the build tree.
-*/
-class Analyzer
-{
-public:
-       enum Mode
-       {
-               DEPS,     //< Skip over "trivial" targets such as InstalledFile
-               ALLDEPS,  //< Print out absolutely every target
-               REBUILD,  //< Print targets that are going to be rebuilt
-               RDEPS     //< Print targets that depend on the given targets
-       };
-
-private:
-       using TableRow = std::vector<std::string>;
-
-       Builder &builder;
-       Mode mode = DEPS;
-       std::vector<TableRow> table;
-       unsigned max_depth = 0;
-       bool full_paths = false;
-       std::map<const Target *, std::set<Target *> > rdepends;
-
-public:
-       Analyzer(Builder &b): builder(b) { }
-
-       void set_mode(Mode m) { mode = m; }
-       void set_max_depth(unsigned m) { max_depth = m; }
-       void set_full_paths(bool f) { full_paths = f; }
-
-       /// Performs the analysis and prints out the resulting dependency tree.
-       void analyze();
-
-private:
-       /** Adds rows for a target, then recursively adds rows for dependencies as
-       needed. */
-       void build_depend_table(Target &, unsigned);
-
-       /// Prints out the table that resulted from the analysis.
-       void print_table() const;
-
-       static bool target_order(const Target *, const Target *);
-       static bool target_order_full(const Target *, const Target *);
-};
-
-#endif
diff --git a/source/androidapplicationcomponent.cpp b/source/androidapplicationcomponent.cpp
deleted file mode 100644 (file)
index e4feaa2..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include "androidapplicationcomponent.h"
-#include "androidmanifestfile.h"
-#include "androidresourcefile.h"
-#include "builder.h"
-#include "installedfile.h"
-#include "sharedlibrary.h"
-#include "sourcepackage.h"
-#include "tool.h"
-#include "toolchain.h"
-
-using namespace std;
-using namespace Msp;
-
-void AndroidApplicationComponent::create_targets() const
-{
-       Builder &builder = package.get_builder();
-       BuildGraph &build_graph = builder.get_build_graph();
-
-       vector<Target *> contents;
-       for(const auto &kvp: build_graph.get_targets())
-               if(kvp.second->get_package()==&package)
-                       if(InstalledFile *inst = dynamic_cast<InstalledFile *>(kvp.second))
-                               contents.push_back(inst->get_real_target());
-
-       AndroidManifestFile *manifest = new AndroidManifestFile(builder, *this);
-       manifest->set_orientation(orientation);
-       for(const string &p: permissions)
-               manifest->add_permission(p);
-
-       vector<Target *> resource_sources;
-       resource_sources.push_back(manifest);
-
-       const Toolchain &toolchain = builder.get_toolchain();
-       Tool &copy = toolchain.get_tool("CP");
-       for(const FS::Path &s: collect_source_files())
-       {
-               Target *tgt = new AndroidResourceFile(builder, *this, s);
-               resource_sources.push_back(copy.create_target(*tgt, "//"));
-       }
-
-       Tool &aapt = toolchain.get_tool("AAPT");
-       Target *resource_bundle = aapt.create_target(resource_sources);
-
-       vector<Target *> apk_sources;
-       apk_sources.reserve(1+contents.size());
-       apk_sources.push_back(resource_bundle);
-
-       const Architecture &arch = package.get_builder().get_current_arch();
-       string lib_dir = format("//%s/lib/", name);
-       if(arch.get_type()=="arm")
-       {
-               lib_dir += "armeabi";
-               if(arch.get_cpu()=="armv7a")
-                       lib_dir += "-v7a";
-       }
-       else
-               lib_dir += arch.get_type();
-
-       string assets_dir = format("//%s/assets", name);
-       for(Target *t: contents)
-       {
-               Target *staged = 0;
-               if(SharedLibrary *shlib = dynamic_cast<SharedLibrary *>(t))
-               {
-                       manifest->set_native_library(shlib);
-                       staged = copy.create_target(*t, lib_dir);
-               }
-               else
-                       staged = copy.create_target(*t, assets_dir);
-               apk_sources.push_back(staged);
-       }
-
-       Tool &apk_builder = toolchain.get_tool("APK");
-       Target *apk = apk_builder.create_target(apk_sources);
-       builder.get_build_graph().add_primary_target(*apk);
-}
-
-
-AndroidApplicationComponent::Loader::Loader(AndroidApplicationComponent &c):
-       DataFile::DerivedObjectLoader<AndroidApplicationComponent, Component::Loader>(c)
-{
-       add("orientation", &AndroidApplicationComponent::orientation);
-       add("permission", &Loader::permission);
-}
-
-void AndroidApplicationComponent::Loader::permission(const string &perm)
-{
-       if(!any_equals(obj.permissions, perm))
-               obj.permissions.push_back(perm);
-}
diff --git a/source/androidapplicationcomponent.h b/source/androidapplicationcomponent.h
deleted file mode 100644 (file)
index 9e19b6d..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef ANDROIDAPPLICATIONCOMPONENT_H_
-#define ANDROIDAPPLICATIONCOMPONENT_H_
-
-#include "component.h"
-
-class AndroidApplicationComponent: public Component
-{
-public:
-       class Loader: public Msp::DataFile::DerivedObjectLoader<AndroidApplicationComponent, Component::Loader>
-       {
-       public:
-               Loader(AndroidApplicationComponent &);
-
-       private:
-               void permission(const std::string &);
-       };
-
-private:
-       std::string orientation;
-       std::vector<std::string> permissions;
-
-public:
-       AndroidApplicationComponent(SourcePackage &p, const std::string &n): Component(p, n) { }
-
-       void create_targets() const override;
-};
-
-#endif
diff --git a/source/androidarchiver.cpp b/source/androidarchiver.cpp
deleted file mode 100644 (file)
index 617445c..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#include "androidarchiver.h"
-#include "androidtools.h"
-
-AndroidArchiver::AndroidArchiver(Builder &b, const Architecture &a, const AndroidNdk &ndk):
-       CustomizedTool(b, "AR", a)
-{
-       set_command("ar", true);
-       if(ndk.get_root_dir().empty())
-               problems.push_back("Android NDK not found");
-       else if(ndk.get_bin_dir().empty())
-               problems.push_back("Android NDK toolchain not found");
-       else
-               set_command((ndk.get_bin_dir()/command).str());
-}
diff --git a/source/androidarchiver.h b/source/androidarchiver.h
deleted file mode 100644 (file)
index a39fe54..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef ANDROIDARCHIVER_H_
-#define ANDROIDARCHIVER_H_
-
-#include "customizedtool.h"
-
-class AndroidNdk;
-
-class AndroidArchiver: public CustomizedTool
-{
-public:
-       AndroidArchiver(Builder &, const Architecture &, const AndroidNdk &);
-};
-
-#endif
diff --git a/source/androidassetpackagingtool.cpp b/source/androidassetpackagingtool.cpp
deleted file mode 100644 (file)
index eb638e9..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/fs/utils.h>
-#include "androidassetpackagingtool.h"
-#include "androidmanifestfile.h"
-#include "androidresourcebundle.h"
-#include "androidtools.h"
-#include "component.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-AndroidAssetPackagingTool::AndroidAssetPackagingTool(Builder &b, const AndroidSdk &s):
-       Tool(b, "AAPT"),
-       sdk(s)
-{
-       if(sdk.get_root_dir().empty())
-               problems.push_back("Android SDK not found");
-       else if(sdk.get_build_tools_dir().empty())
-               problems.push_back("Android build-tools not found");
-       else
-               set_command((sdk.get_build_tools_dir()/"aapt").str());
-
-       if(sdk.get_platform_jar().empty())
-               problems.push_back("Android platform not found");
-
-       set_run_external(_run);
-}
-
-Target *AndroidAssetPackagingTool::create_target(const vector<Target *> &sources, const string &)
-{
-       AndroidManifestFile *manifest = 0;
-       vector<FileTarget *> resources;
-       resources.reserve(sources.size());
-       for(Target *s: sources)
-       {
-               if(AndroidManifestFile *m = dynamic_cast<AndroidManifestFile *>(s))
-                       manifest = m;
-               else if(FileTarget *f = dynamic_cast<FileTarget *>(s))
-                       resources.push_back(f);
-       }
-
-       if(!manifest)
-               throw invalid_argument("AndroidAssetPackagingTool::create_target");
-
-       AndroidResourceBundle *res = new AndroidResourceBundle(builder, *manifest->get_component(), *manifest, resources);
-       res->set_tool(*this);
-       return res;
-}
-
-ExternalTask::Arguments AndroidAssetPackagingTool::_run(const AndroidResourceBundle &res, FS::Path &work_dir)
-{
-       const AndroidAssetPackagingTool &tool = dynamic_cast<const AndroidAssetPackagingTool &>(*res.get_tool());
-
-       ExternalTask::Arguments argv;
-       argv.push_back(tool.get_executable()->get_path().str());
-       argv.push_back("package");
-
-       argv.push_back("-I");
-       argv.push_back(tool.sdk.get_platform_jar().str());
-
-       argv.push_back("-F");
-       argv.push_back(FS::relative(res.get_path(), work_dir).str());
-
-       vector<FS::Path> resource_dirs;
-       resource_dirs.reserve(res.get_dependencies().size());
-       for(Target *d: res.get_dependencies())
-       {
-               FileTarget *file = dynamic_cast<FileTarget *>(d);
-               Target *real = d->get_real_target();
-
-               if(dynamic_cast<AndroidManifestFile *>(real))
-               {
-                       argv.push_back("-M");
-                       argv.push_back(FS::relative(file->get_path(), work_dir).str());
-               }
-               else if(real->get_package()==res.get_package())
-               {
-                       const FS::Path &path = file->get_path();
-                       FS::Path res_dir = path.subpath(0, path.size()-2);
-                       if(!any_equals(resource_dirs, res_dir))
-                               resource_dirs.push_back(res_dir);
-               }
-       }
-
-       for(const FS::Path &d: resource_dirs)
-       {
-               argv.push_back("-S");
-               argv.push_back(FS::relative(d, work_dir).str());
-       }
-
-       return argv;
-}
diff --git a/source/androidassetpackagingtool.h b/source/androidassetpackagingtool.h
deleted file mode 100644 (file)
index 252870f..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef ANDROIDASSETPACKAGINGTOOL_H_
-#define ANDROIDASSETPACKAGINGTOOL_H_
-
-#include "tool.h"
-
-class AndroidResourceBundle;
-class AndroidSdk;
-
-class AndroidAssetPackagingTool: public Tool
-{
-private:
-       const AndroidSdk &sdk;
-
-public:
-       AndroidAssetPackagingTool(Builder &, const AndroidSdk &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static ExternalTask::Arguments _run(const AndroidResourceBundle &, Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/androidcompiler.cpp b/source/androidcompiler.cpp
deleted file mode 100644 (file)
index 2321f03..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-#include <msp/fs/dir.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include <msp/strings/utils.h>
-#include "androidcompiler.h"
-#include "androidtools.h"
-#include "builder.h"
-#include "externaltask.h"
-#include "filetarget.h"
-
-using namespace std;
-using namespace Msp;
-
-AndroidCompiler::AndroidCompiler(Builder &b, const Architecture &a, const string &t, const AndroidNdk &n):
-       CustomizedTool(b, t, a),
-       ndk(n)
-{
-       set_command((tag=="CXX" ? "g++" : "gcc"), true);
-       if(ndk.get_root_dir().empty())
-               problems.push_back("Android NDK not found");
-       else if(ndk.get_bin_dir().empty())
-               problems.push_back("Android NDK toolchain not found");
-       else
-               set_command((ndk.get_bin_dir()/command).str());
-
-       if(ndk.get_platform_sysroot().empty())
-               problems.push_back("Android platform not found");
-       else if(!ndk.get_common_sysroot().empty())
-       {
-               build_info.sysroot = ndk.get_common_sysroot();
-               /* The common sysroot has asm headers in arch directories and the
-               compiler doesn't pick them up automatically */
-               build_info.incpath.push_back(ndk.get_common_sysroot()/"usr/include"/architecture->get_cross_prefix());
-       }
-       else
-               build_info.sysroot = ndk.get_platform_sysroot();
-}
-
-void AndroidCompiler::do_prepare(ToolData &tool) const
-{
-       const Architecture &arch = *static_cast<const Tool &>(tool).get_architecture();
-
-       CustomizedTool::do_prepare(tool);
-       if(tag=="CXX")
-       {
-               tool.build_info.libs.push_back("gnustl_static");
-
-               unsigned version = tool.extra_data;
-               string version_str = format("%d.%d.%d", version>>16, (version>>8)&0xFF, version&0xFF);
-               FS::Path libstdcxx_dir = ndk.get_root_dir()/"sources"/"cxx-stl"/"gnu-libstdc++";
-               FS::Path libstdcxx_path;
-               while(1)
-               {
-                       libstdcxx_path = libstdcxx_dir/version_str;
-                       if(FS::exists(libstdcxx_path))
-                               break;
-
-                       string::size_type dot = version_str.rfind('.');
-                       if(dot==string::npos)
-                       {
-                               tool.problems.push_back("C++ standard library not found");
-                               return;
-                       }
-
-                       version_str = version_str.substr(0, dot);
-               }
-
-               builder.get_logger().log("tools", "Found GNU libstdc++ in %s", libstdcxx_path);
-
-               FS::Path public_dir = libstdcxx_path/"include";
-               tool.system_path.push_back(public_dir);
-               tool.build_info.incpath.push_back(public_dir);
-
-               FS::Path arch_path = libstdcxx_path/"libs";
-               builder.get_logger().log("files", "Traversing %s", arch_path.str());
-               string arch_dir = arch.best_match(list_files(arch_path));
-               if(!arch_dir.empty())
-               {
-                       tool.build_info.incpath.push_back(libstdcxx_path/"libs"/arch_dir/"include");
-                       tool.build_info.libpath.push_back(libstdcxx_path/"libs"/arch_dir);
-               }
-       }
-}
diff --git a/source/androidcompiler.h b/source/androidcompiler.h
deleted file mode 100644 (file)
index 86dfa27..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef ANDROIDCOMPILER_H_
-#define ANDROIDCOMPILER_H_
-
-#include "customizedtool.h"
-
-class AndroidNdk;
-
-class AndroidCompiler: public CustomizedTool
-{
-private:
-       const AndroidNdk &ndk;
-
-public:
-       AndroidCompiler(Builder &, const Architecture &, const std::string &, const AndroidNdk &);
-
-protected:
-       void do_prepare(ToolData &) const override;
-};
-
-#endif
diff --git a/source/androidlinker.cpp b/source/androidlinker.cpp
deleted file mode 100644 (file)
index ec90249..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "androidlinker.h"
-#include "androidtools.h"
-
-using namespace std;
-
-AndroidLinker::AndroidLinker(Builder &b, const Architecture &a, const AndroidNdk &ndk):
-       CustomizedTool(b, "LINK", a)
-{
-       build_info.sysroot = ndk.get_platform_sysroot();
-
-       set_command("gcc", true);
-       set_command((ndk.get_bin_dir()/command).str());
-}
-
-Target *AndroidLinker::create_target(const vector<Target *> &sources, const string &)
-{
-       return CustomizedTool::create_target(sources, "shared");
-}
diff --git a/source/androidlinker.h b/source/androidlinker.h
deleted file mode 100644 (file)
index 7418d3c..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef ANDROIDLINKER_H_
-#define ANDROIDLINKER_H_
-
-#include "customizedtool.h"
-
-class AndroidNdk;
-
-class AndroidLinker: public CustomizedTool
-{
-public:
-       AndroidLinker(Builder &, const Architecture &, const AndroidNdk &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-};
-
-#endif
diff --git a/source/androidmanifestfile.cpp b/source/androidmanifestfile.cpp
deleted file mode 100644 (file)
index 2dd1c96..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#include <msp/core/algorithm.h>
-#include "androidapplicationcomponent.h"
-#include "androidmanifestfile.h"
-#include "builder.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-AndroidManifestFile::AndroidManifestFile(Builder &b, const AndroidApplicationComponent &a):
-       FileTarget(b, a.get_package(), a.get_package().get_temp_directory()/a.get_name()/"AndroidManifest.xml")
-{
-       component = &a;
-       tool = &builder.get_toolchain().get_tool("AMG");
-
-       add_dependency(package->get_build_file());
-}
-
-void AndroidManifestFile::set_native_library(SharedLibrary *lib)
-{
-       native_lib = lib;
-}
-
-void AndroidManifestFile::set_orientation(const string &ori)
-{
-       orientation = ori;
-}
-
-void AndroidManifestFile::add_permission(const string &perm)
-{
-       if(!any_equals(permissions, perm))
-               permissions.push_back(perm);
-}
diff --git a/source/androidmanifestfile.h b/source/androidmanifestfile.h
deleted file mode 100644 (file)
index 9c0ed21..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef ANDROIDMANIFESTFILE_H_
-#define ANDROIDMANIFESTFILE_H_
-
-#include <vector>
-#include "filetarget.h"
-
-class AndroidApplicationComponent;
-class SharedLibrary;
-
-/**
-Metadata file for an Android application.
-*/
-class AndroidManifestFile: public FileTarget
-{
-private:
-       SharedLibrary *native_lib = 0;
-       std::vector<std::string> permissions;
-       std::string orientation;
-
-public:
-       AndroidManifestFile(Builder &, const AndroidApplicationComponent &);
-
-       const char *get_type() const override { return "AndroidManifestFile"; }
-
-       void set_native_library(SharedLibrary *);
-       SharedLibrary *get_native_library() const { return native_lib; }
-
-       void add_permission(const std::string &);
-       void set_orientation(const std::string &);
-       const std::vector<std::string> &get_permissions() const { return permissions; }
-       const std::string &get_orientation() const { return orientation; }
-};
-
-#endif
diff --git a/source/androidmanifestgenerator.cpp b/source/androidmanifestgenerator.cpp
deleted file mode 100644 (file)
index 0c48982..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include "androidmanifestfile.h"
-#include "androidmanifestgenerator.h"
-#include "component.h"
-#include "internaltask.h"
-#include "sharedlibrary.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-AndroidManifestGenerator::AndroidManifestGenerator(Builder &b):
-       Tool(b, "AMG")
-{
-       set_run_internal(_run);
-}
-
-Target *AndroidManifestGenerator::create_target(const vector<Target *> &, const string &)
-{
-       throw logic_error("not implemented");
-}
-
-bool AndroidManifestGenerator::_run(const AndroidManifestFile &manifest)
-{
-       const Component &comp = *manifest.get_component();
-       const SourcePackage &pkg = comp.get_package();
-
-       BuildInfo binfo;
-       manifest.collect_build_info(binfo);
-
-       IO::BufferedFile out(manifest.get_path().str(), IO::M_WRITE);
-       out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
-       IO::print(out, "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"%s\">\n", comp.get_name());
-       out.write("\t<uses-sdk android:minSdkVersion=\"9\" />\n");
-       // TODO Make the icon name configurable
-       bool debuggable = binfo.debug;
-       IO::print(out, "\t<application android:icon=\"@drawable/icon\" android:label=\"%s\" android:hasCode=\"false\" android:debuggable=\"%s\">\n", pkg.get_label(), debuggable);
-       if(SharedLibrary *native_lib = manifest.get_native_library())
-       {
-               out.write("\t\t<activity android:name=\"android.app.NativeActivity\"");
-               const string &orientation = manifest.get_orientation();
-               if(!orientation.empty())
-                       IO::print(out, " android:screenOrientation=\"%s\"", orientation);
-               out.write(">\n");
-               IO::print(out, "\t\t\t<meta-data android:name=\"android.app.lib_name\" android:value=\"%s\" />\n", native_lib->get_libname());
-               out.write("\t\t\t<intent-filter>\n");
-               out.write("\t\t\t\t<action android:name=\"android.intent.action.MAIN\" />\n");
-               out.write("\t\t\t\t<category android:name=\"android.intent.category.LAUNCHER\" />\n");
-               out.write("\t\t\t</intent-filter>\n");
-               out.write("\t\t</activity>\n");
-       }
-       out.write("\t</application>\n");
-       for(const string &p: manifest.get_permissions())
-               IO::print(out, "\t<uses-permission android:name=\"%s\" />\n", p);
-       out.write("</manifest>\n");
-
-       return true;
-}
diff --git a/source/androidmanifestgenerator.h b/source/androidmanifestgenerator.h
deleted file mode 100644 (file)
index 58fc0b2..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef ANDROIDMANIFESTGENERATOR_H_
-#define ANDROIDMANIFESTGENERATOR_H_
-
-#include "tool.h"
-
-class AndroidManifestFile;
-
-class AndroidManifestGenerator: public Tool
-{
-public:
-       AndroidManifestGenerator(Builder &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static bool _run(const AndroidManifestFile &);
-};
-
-#endif
diff --git a/source/androidpackagefile.cpp b/source/androidpackagefile.cpp
deleted file mode 100644 (file)
index 9dee78f..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#include "androidpackagefile.h"
-#include "androidresourcebundle.h"
-#include "component.h"
-#include "sourcepackage.h"
-
-using namespace std;
-
-AndroidPackageFile::AndroidPackageFile(Builder &b, const Component &c, AndroidResourceBundle &resource_bundle, const vector<FileTarget *> &other_files):
-       FileTarget(b, c.get_package(), c.get_package().get_output_directory()/(c.get_name()+".apk"))
-{
-       component = &c;
-
-       add_dependency(resource_bundle);
-       for(FileTarget *f: other_files)
-               add_dependency(*f);
-}
diff --git a/source/androidpackagefile.h b/source/androidpackagefile.h
deleted file mode 100644 (file)
index c5f237a..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef ANDROIDPACKAGEFILE_H_
-#define ANDROIDPACKAGEFILE_H_
-
-#include "filetarget.h"
-
-class AndroidResourceBundle;
-
-class AndroidPackageFile: public FileTarget
-{
-public:
-       AndroidPackageFile(Builder &, const Component &, AndroidResourceBundle &, const std::vector<FileTarget *> &);
-
-       const char *get_type() const override { return "AndroidPackageFile"; }
-};
-
-#endif
diff --git a/source/androidresourcebundle.cpp b/source/androidresourcebundle.cpp
deleted file mode 100644 (file)
index 8113c00..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#include "androidmanifestfile.h"
-#include "androidresourcebundle.h"
-#include "component.h"
-#include "sourcepackage.h"
-
-using namespace std;
-
-AndroidResourceBundle::AndroidResourceBundle(Builder &b, const Component &c, AndroidManifestFile &manifest, const vector<FileTarget *> &resources):
-       FileTarget(b, c.get_package(), c.get_package().get_temp_directory()/c.get_name()/(c.get_name()+".ap_"))
-{
-       component = &c;
-
-       add_dependency(manifest);
-       for(FileTarget *f: resources)
-               add_dependency(*f);
-}
diff --git a/source/androidresourcebundle.h b/source/androidresourcebundle.h
deleted file mode 100644 (file)
index 18c07e0..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef ANDROIDRESOURCEBUNDLE_H_
-#define ANDROIDRESOURCEBUNDLE_H_
-
-#include "filetarget.h"
-
-class AndroidManifestFile;
-
-class AndroidResourceBundle: public FileTarget
-{
-public:
-       AndroidResourceBundle(Builder &, const Component &, AndroidManifestFile &, const std::vector<FileTarget *> &);
-
-       const char *get_type() const override { return "AndroidResourceBundle"; }
-};
-
-#endif
diff --git a/source/androidresourcefile.cpp b/source/androidresourcefile.cpp
deleted file mode 100644 (file)
index d88f786..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#include <msp/fs/utils.h>
-#include "androidresourcefile.h"
-#include "component.h"
-
-using namespace std;
-using namespace Msp;
-
-AndroidResourceFile::AndroidResourceFile(Builder &b, const Component &c, const FS::Path &p):
-       FileTarget(b, c.get_package(), p)
-{
-       string ext = FS::extpart(FS::basename(p));
-       if(ext==".png" || ext==".jpg")
-               resource_type = "drawable";
-       // TODO recognize various xml files; must inspect contents
-       else
-               resource_type = "raw";
-
-       install_location = "res/"+resource_type;
-}
diff --git a/source/androidresourcefile.h b/source/androidresourcefile.h
deleted file mode 100644 (file)
index 162026c..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef ANDROIDRESOURCEFILE_H_
-#define ANDROIDRESOURCEFILE_H_
-
-#include "filetarget.h"
-
-/**
-A file destined to be used as a resource in an Android application.
-*/
-class AndroidResourceFile: public FileTarget
-{
-private:
-       std::string resource_type;
-
-public:
-       AndroidResourceFile(Builder &, const Component &, const Msp::FS::Path &);
-
-       const char *get_type() const override { return "AndroidResourceFile"; }
-};
-
-#endif
diff --git a/source/androidtools.cpp b/source/androidtools.cpp
deleted file mode 100644 (file)
index e086dd0..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/core/environ.h>
-#include <msp/fs/dir.h>
-#include <msp/fs/stat.h>
-#include <msp/strings/format.h>
-#include <msp/strings/lexicalcast.h>
-#include <msp/strings/utils.h>
-#include "androidarchiver.h"
-#include "androidassetpackagingtool.h"
-#include "androidcompiler.h"
-#include "androidlinker.h"
-#include "androidmanifestgenerator.h"
-#include "androidtools.h"
-#include "apkbuilder.h"
-#include "architecture.h"
-#include "builder.h"
-#include "jarsigner.h"
-
-using namespace std;
-using namespace Msp;
-
-// TODO Mark problems somewhere instead of throwing exceptions
-
-unsigned parse_version(const std::string &version_str)
-{
-       vector<string> version_parts = split(version_str, '.');
-       unsigned version = lexical_cast<unsigned>(version_parts[0])<<16;
-       if(version_parts.size()>1)
-               version += lexical_cast<unsigned>(version_parts[1])<<8;
-       if(version_parts.size()>2)
-               version += lexical_cast<unsigned>(version_parts[2]);
-       return version;
-}
-
-
-AndroidDevKit::AndroidDevKit(Builder &b, const string &type, const FS::Path &default_path):
-       builder(b)
-{
-       string var = format("ANDROID_%s_ROOT", type);
-       root = getenv(var);
-       if(root.empty())
-       {
-               if(!default_path.empty() && FS::exists(default_path))
-                       root = default_path;
-               else
-               {
-                       builder.get_logger().log("problems", "Android %s not found", type);
-                       return;
-               }
-       }
-
-       FS::Path platforms_dir = root/"platforms";
-       if(!FS::exists(platforms_dir))
-               return;
-
-       builder.get_logger().log("files", "Traversing %s", platforms_dir.str());
-       for(const string &p: list_filtered(platforms_dir, "^android-[1-9][0-9]*$"))
-       {
-               unsigned api = lexical_cast<unsigned>(p.substr(8));
-               if(api>63)
-                       builder.get_logger().log("problems", "API level %d is too high", api);
-               else
-                       supported_api_levels |= static_cast<uint64_t>(1)<<api;
-       }
-}
-
-void AndroidDevKit::select_api_level(unsigned api)
-{
-       if(!((supported_api_levels>>api)&1))
-               throw invalid_argument("AndroidDevKit::select_api_level");
-
-       init_api_level(api);
-}
-
-
-AndroidSdk::AndroidSdk(Builder &b):
-       AndroidDevKit(b, "SDK")
-{
-       find_build_tools_dir();
-}
-
-void AndroidSdk::find_build_tools_dir()
-{
-       FS::Path bt_dir = root/"build-tools";
-       if(!FS::exists(bt_dir))
-       {
-               builder.get_logger().log("problems", "Android build-tools not found");
-               return;
-       }
-
-       builder.get_logger().log("files", "Traversing %s", bt_dir.str());
-       string use_tools;
-       unsigned latest_version = 0;
-       for(const string &v: list_files(bt_dir))
-       {
-               unsigned version = parse_version(v);
-               if(version>latest_version)
-               {
-                       use_tools = v;
-                       latest_version = version;
-               }
-       }
-
-       if(use_tools.empty())
-       {
-               builder.get_logger().log("problems", "Android build-tools not found");
-               return;
-       }
-
-       build_tools_dir = bt_dir/use_tools;
-       builder.get_logger().log("tools", "Android build-tools found in %s", build_tools_dir.str());
-}
-
-void AndroidSdk::init_api_level(unsigned api)
-{
-       platform_jar = root/"platforms"/format("android-%d", api)/"android.jar";
-}
-
-
-AndroidNdk::AndroidNdk(Builder &b, const Architecture &a, const AndroidSdk &sdk):
-       AndroidDevKit(b, "NDK", create_default_path(sdk)),
-       architecture(a)
-{
-       if(!root.empty())
-       {
-               FS::Path csr = root/"sysroot";
-               if(FS::exists(csr))
-               {
-                       common_sysroot = csr;
-                       builder.get_logger().log("tools", "Android NDK common sysroot is %s", common_sysroot);
-               }
-       }
-
-       find_toolchain_dir();
-}
-
-FS::Path AndroidNdk::create_default_path(const AndroidSdk &sdk)
-{
-       if(sdk.get_root_dir().empty())
-               return FS::Path();
-       return sdk.get_root_dir()/"ndk-bundle";
-}
-
-void AndroidNdk::find_toolchain_dir()
-{
-       if(root.empty())
-               return;
-
-       FS::Path toolchains_dir = root/"toolchains";
-       if(!FS::exists(toolchains_dir))
-       {
-               builder.get_logger().log("problems", "Android NDK toolchains not found");
-               return;
-       }
-
-       builder.get_logger().log("files", "Traversing %s", toolchains_dir.str());
-       string prefix = architecture.get_cross_prefix()+"-";
-       string use_toolchain;
-       unsigned latest_version = 0;
-       for(const string &t: list_filtered(toolchains_dir, "^"+prefix))
-       {
-               string version_str = t.substr(prefix.size());
-               string compiler = "gcc";
-               if(!isdigit(version_str[0]))
-               {
-                       unsigned j;
-                       for(j=1; (j<version_str.size() && !isdigit(version_str[j])); ++j) ;
-                       compiler = version_str.substr(0, j);
-                       version_str = version_str.substr(j);
-               }
-
-               if(compiler!="gcc")
-                       continue;
-
-               unsigned version = parse_version(version_str);
-               if(version>latest_version)
-               {
-                       use_toolchain = t;
-                       latest_version = version;
-               }
-       }
-
-       if(use_toolchain.empty())
-       {
-               builder.get_logger().log("problems", "No applicable Android NDK toolchains found");
-               return;
-       }
-
-       const Architecture &native_arch = builder.get_native_arch();
-
-       FS::Path tc_archs_dir = toolchains_dir/use_toolchain/"prebuilt";
-       builder.get_logger().log("files", "Traversing %s", tc_archs_dir.str());
-       string use_arch = native_arch.best_match(list_files(tc_archs_dir));
-
-       if(use_arch.empty())
-       {
-               builder.get_logger().log("problems", "No applicable Android NDK toolchains found");
-               return;
-       }
-
-       bin_dir = toolchains_dir/use_toolchain/"prebuilt"/use_arch/"bin";
-       builder.get_logger().log("tools", "Android NDK toolchain binaries found in %s", bin_dir.str());
-}
-
-void AndroidNdk::init_api_level(unsigned api)
-{
-       FS::Path platform_archs_dir = root/"platforms"/format("android-%d", api);
-       builder.get_logger().log("files", "Traversing %s", platform_archs_dir.str());
-       vector<string> platform_archs = list_filtered(platform_archs_dir, "^arch-");
-       for(string &a: platform_archs)
-               a.erase(0, 5);
-       string use_arch = architecture.best_match(platform_archs);
-
-       if(use_arch.empty())
-       {
-               builder.get_logger().log("problems", "No matching Android NDK platform found");
-               return;
-       }
-
-       platform_sysroot = platform_archs_dir/("arch-"+use_arch);
-       builder.get_logger().log("tools", "Android NDK platform sysroot is %s", platform_sysroot);
-}
-
-
-AndroidTools::AndroidTools(Builder &builder, const Architecture &arch):
-       Toolchain("android-gnu", get_priority(arch)),
-       sdk(builder),
-       ndk(builder, arch, sdk)
-{
-       if(uint64_t common_api_levels = sdk.get_supported_api_levels()&ndk.get_supported_api_levels())
-       {
-               unsigned api = 0;
-               for(unsigned i=32; i>0; i>>=1)
-                       api += i*!!(common_api_levels>>(api+i));
-               builder.get_logger().log("tools", "Using Android API level %d", api);
-               sdk.select_api_level(api);
-               ndk.select_api_level(api);
-       }
-       else
-               builder.get_logger().log("problems", "No usable Android platforms found");
-
-       add_tool(new AndroidCompiler(builder, arch, "CC", ndk));
-       add_tool(new AndroidCompiler(builder, arch, "CXX", ndk));
-       add_tool(new AndroidLinker(builder, arch, ndk));
-       add_tool(new AndroidArchiver(builder, arch, ndk));
-       add_tool(new AndroidManifestGenerator(builder));
-       add_tool(new AndroidAssetPackagingTool(builder, sdk));
-       add_tool(new ApkBuilder(builder));
-       add_tool(new JarSigner(builder));
-}
-
-int AndroidTools::get_priority(const Architecture &arch)
-{
-       if(arch.get_system()=="android")
-               return 30;
-       else
-               return -10;
-}
diff --git a/source/androidtools.h b/source/androidtools.h
deleted file mode 100644 (file)
index a88cdd3..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-#ifndef ANDROIDTOOLS_H_
-#define ANDROIDTOOLS_H_
-
-#include <cstdint>
-#include <msp/fs/path.h>
-#include "toolchain.h"
-
-class Architecture;
-class Builder;
-
-class AndroidDevKit
-{
-protected:
-       Builder &builder;
-       Msp::FS::Path root;
-       /* Needs refactoring if API levels go over 63.  At present rate this will
-       take decades to occur. */
-       uint64_t supported_api_levels = 0;
-
-       AndroidDevKit(Builder &, const std::string &, const Msp::FS::Path & = Msp::FS::Path());
-       ~AndroidDevKit() { }
-
-public:
-       const Msp::FS::Path &get_root_dir() const { return root; }
-       uint64_t get_supported_api_levels() const { return supported_api_levels; }
-       void select_api_level(unsigned);
-protected:
-       virtual void init_api_level(unsigned) = 0;
-};
-
-class AndroidSdk: public AndroidDevKit
-{
-private:
-       Msp::FS::Path build_tools_dir;
-       // TODO use a FileTarget for the jar
-       Msp::FS::Path platform_jar;
-
-public:
-       AndroidSdk(Builder &);
-
-private:
-       void find_build_tools_dir();
-       void init_api_level(unsigned) override;
-
-public:
-       const Msp::FS::Path &get_build_tools_dir() const { return build_tools_dir; }
-       const Msp::FS::Path &get_platform_jar() const { return platform_jar; }
-};
-
-class AndroidNdk: public AndroidDevKit
-{
-private:
-       const Architecture &architecture;
-       Msp::FS::Path bin_dir;
-       Msp::FS::Path common_sysroot;
-       Msp::FS::Path platform_sysroot;
-
-public:
-       AndroidNdk(Builder &, const Architecture &, const AndroidSdk &);
-private:
-       static Msp::FS::Path create_default_path(const AndroidSdk &);
-
-       void find_toolchain_dir();
-       void init_api_level(unsigned) override;
-
-public:
-       const Msp::FS::Path &get_bin_dir() const { return bin_dir; }
-       const Msp::FS::Path &get_common_sysroot() const { return common_sysroot; }
-       const Msp::FS::Path &get_platform_sysroot() const { return platform_sysroot; }
-};
-
-
-class AndroidTools: public Toolchain
-{
-private:
-       AndroidSdk sdk;
-       AndroidNdk ndk;
-
-public:
-       AndroidTools(Builder &, const Architecture &);
-
-       static int get_priority(const Architecture &);
-};
-
-#endif
diff --git a/source/apkbuilder.cpp b/source/apkbuilder.cpp
deleted file mode 100644 (file)
index f62e485..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-#include <msp/fs/utils.h>
-#include "androidpackagefile.h"
-#include "androidresourcebundle.h"
-#include "apkbuilder.h"
-#include "builder.h"
-#include "chainedtask.h"
-#include "component.h"
-#include "externaltask.h"
-#include "filetarget.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-// TODO Separate jar into its own tool and have this one just chain the two
-
-ApkBuilder::ApkBuilder(Builder &b):
-       Tool(b, "APK")
-{
-       set_command("jar");
-       set_run(_run);
-}
-
-Target *ApkBuilder::create_target(const vector<Target *> &sources, const string &)
-{
-       AndroidResourceBundle *resource_bundle = 0;
-       vector<FileTarget *> other_files;
-       other_files.reserve(sources.size());
-       for(Target *s: sources)
-       {
-               if(AndroidResourceBundle *r = dynamic_cast<AndroidResourceBundle *>(s))
-                       resource_bundle = r;
-               else if(FileTarget *f = dynamic_cast<FileTarget *>(s))
-                       other_files.push_back(f);
-       }
-       AndroidPackageFile *apk = new AndroidPackageFile(builder, *resource_bundle->get_component(), *resource_bundle, other_files);
-       apk->set_tool(*this);
-       return apk;
-}
-
-void ApkBuilder::do_prepare(ToolData &tool) const
-{
-       Tool *jarsigner = &builder.get_toolchain().get_tool("JSGN");
-       jarsigner->prepare();
-       tool.extra_data = jarsigner;
-}
-
-Task *ApkBuilder::_run(const Target &tgt)
-{
-       const AndroidPackageFile &apk = dynamic_cast<const AndroidPackageFile &>(tgt);
-       const ApkBuilder &tool = dynamic_cast<const ApkBuilder &>(*apk.get_tool());
-
-       ExternalTask::Arguments argv;
-       argv.push_back(tool.get_executable()->get_path().str());
-       argv.push_back("u");
-
-       FS::Path input_path;
-       vector<FS::Path> files;
-       files.reserve(apk.get_dependencies().size());
-       for(Target *d: apk.get_dependencies())
-       {
-               FileTarget *file = dynamic_cast<FileTarget *>(d);
-               Target *real = d->get_real_target();
-
-               if(dynamic_cast<AndroidResourceBundle *>(real))
-                       input_path = file->get_path();
-               else if(real->get_package()==apk.get_package())
-                       files.push_back(file->get_path());
-       }
-
-       FS::Path work_dir = FS::dirname(input_path);
-
-       for(const FS::Path &f: files)
-               argv.push_back(FS::relative(f, work_dir).str());
-
-       ExternalTask *task = new ExternalTask(argv, work_dir);
-       task->set_stdin(FS::basename(input_path));
-       task->set_stdout(FS::relative(apk.get_path(), work_dir));
-       ChainedTask *chain = new ChainedTask(task);
-       chain->add_task(tool.extra_data.value<Tool *>()->run(apk));
-       return chain;
-}
diff --git a/source/apkbuilder.h b/source/apkbuilder.h
deleted file mode 100644 (file)
index 8eaaeef..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef APKBUILDER_H_
-#define APKBUILDER_H_
-
-#include "tool.h"
-
-class AndroidPackageFile;
-
-class ApkBuilder: public Tool
-{
-public:
-       ApkBuilder(Builder &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-protected:
-       void do_prepare(ToolData &) const override;
-
-private:
-       static Task *_run(const Target &);
-};
-
-#endif
diff --git a/source/architecture.cpp b/source/architecture.cpp
deleted file mode 100644 (file)
index 54ffcf0..0000000
+++ /dev/null
@@ -1,337 +0,0 @@
-#include <limits>
-#include <msp/strings/format.h>
-#include <msp/strings/utils.h>
-#include "architecture.h"
-#include "builder.h"
-#include "executable.h"
-#include "importlibrary.h"
-#include "objectfile.h"
-#include "sharedlibrary.h"
-#include "staticlibrary.h"
-#include "sysutils.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace {
-
-const char *types[] =
-{
-       "x86",
-       "arm",
-       "ppc",
-       0
-};
-
-const char *cpus[] =
-{
-       "i386",       "x86",
-       "i486",       "x86",
-       "pentium",    "x86",
-       "pentiumpro", "x86",
-       "pentium2",   "x86",
-       "pentium3",   "x86",
-       "pentium4",   "x86",
-       "core2",      "x86",
-       "nehalem",    "x86",
-       "k6",         "x86",
-       "athlon",     "x86",
-       "athlonxp",   "x86",
-       "athlon64",   "x86",
-       "armv5",      "arm",
-       "armv6",      "arm",
-       "armv7",      "arm",
-       "armv7a",     "arm",
-       0
-};
-
-const char *fpus[] =
-{
-       "387",   "x86",
-       "sse",   "x86",
-       "sse3",  "x86",
-       "sse4.1", "x86",
-       "vfpv3", "arm",
-       "neon",  "arm",
-       0
-};
-
-const char *systems[] =
-{
-       "linux",
-       "freebsd",
-       "darwin",
-       "windows",
-       "android",
-       0
-};
-
-const char *toolchains[] =
-{
-       "gnu",
-       "clang",
-       "msvc",
-       0
-};
-
-const char *aliases[] =
-{
-       "pc",              "x86",
-       "x86_64",          "x86-64",
-       "x64",             "x86-64",
-       "amd64",           "x86-64",
-       "i586",            "pentium",
-       "i686",            "pentiumpro",
-       "corei7",          "nehalem",
-       "win32",           "windows-32",
-       "win64",           "windows-64",
-       "power macintosh", "ppc",
-       "armeabi",         "arm",
-       "v7a",             "armv7a",
-       "gcc",             "gnu",
-       "mingw",           "windows-gnu",
-       0
-};
-
-}
-
-Architecture::Architecture(Builder &b, const string &spec):
-       builder(b)
-{
-       if(spec.empty())
-       {
-               parse_specification(get_system_type());
-               // We really only want to set type for the default arch
-               cpu.clear();
-               bits = sizeof(void *)*numeric_limits<unsigned char>::digits;
-               native = true;
-       }
-       else
-       {
-               parse_specification(spec);
-               const Architecture &native_arch = builder.get_native_arch();
-               if(type.empty())
-                       type = native_arch.type;
-               if(system.empty())
-                       system = native_arch.system;
-               if(!bits)
-               {
-                       if(type==native_arch.type)
-                               bits = native_arch.bits;
-                       else
-                               bits = 32;
-               }
-
-               if(type!=native_arch.type || system!=native_arch.system)
-                       cross_prefix = format("%s-%s", type, system);
-               else if(bits==native_arch.bits)
-                       native = true;
-       }
-
-       update();
-}
-
-void Architecture::refine(const string &spec)
-{
-       parse_specification(spec);
-       update();
-}
-
-void Architecture::update()
-{
-       name = type;
-       if(!cpu.empty())
-               name += format("-%s", cpu);
-       if(!fpu.empty())
-               name += format("-%s", fpu);
-       name += format("-%d-%s", bits, system);
-       if(!toolchain.empty())
-               name += format("-%s", toolchain);
-
-       filename_patterns.clear();
-       if(system=="windows")
-       {
-               add_pattern<SharedLibrary>("%.dll");
-               if(toolchain=="msvc")
-               {
-                       add_pattern<ObjectFile>("%.obj");
-                       add_pattern<ImportLibrary>("%.lib");
-                       add_pattern<StaticLibrary>("%_static.lib");
-               }
-               else
-               {
-                       add_pattern<ObjectFile>("%.o");
-                       add_pattern<SharedLibrary>("lib%.dll");
-                       add_pattern<ImportLibrary>("lib%.dll.a");
-                       add_pattern<StaticLibrary>("lib%.a");
-               }
-               add_pattern<Executable>("%.exe");
-       }
-       else
-       {
-               add_pattern<ObjectFile>("%.o");
-               if(system=="darwin")
-                       add_pattern<SharedLibrary>("lib%.dylib");
-               else
-                       add_pattern<SharedLibrary>("lib%.so");
-               add_pattern<StaticLibrary>("lib%.a");
-               add_pattern<Executable>("%");
-       }
-}
-
-bool Architecture::match_name(const string &pattern, unsigned *quality) const
-{
-       bool negate = (pattern[0]=='!');
-       vector<string> parts = split(pattern.substr(negate), "-");
-       resolve_aliases(parts);
-       for(const string &p: parts)
-       {
-               if((p=="32" && bits==32) || (p=="64" && bits==64))
-                       ;
-               else if(p!=type && p!=cpu && p!=fpu && p!=system && p!=toolchain)
-               {
-                       if(quality)
-                               *quality = 0;
-                       return negate;
-               }
-       }
-
-       if(quality)
-               *quality = parts.size();
-       return !negate;
-}
-
-string Architecture::best_match(const vector<string> &names) const
-{
-       string best;
-       unsigned best_quality = 0;
-       for(const string &n: names)
-       {
-               unsigned quality;
-               if(match_name(n, &quality))
-                       if(quality>best_quality)
-                       {
-                               best = n;
-                               best_quality = quality;
-                       }
-       }
-
-       return best;
-}
-
-template<typename T>
-void Architecture::add_pattern(const string &pat)
-{
-       filename_patterns[typeid(T).name()].push_back(Pattern(pat));
-}
-
-void Architecture::resolve_aliases(vector<string> &parts)
-{
-       for(unsigned i=0; i<parts.size(); ++i)
-       {
-               const string &part = parts[i];
-               const char *replace = 0;
-               for(unsigned j=0; (!replace && aliases[j]); j+=2)
-                       if(part==aliases[j])
-                               replace = aliases[j+1];
-
-               if(replace)
-               {
-                       bool has_dash = false;
-                       for(const char *c=replace; (!has_dash && *c); ++c)
-                               has_dash = (*c=='-');
-
-                       if(has_dash)
-                       {
-                               vector<string> rparts = split(replace, "-");
-                               parts[i] = rparts[0];
-                               parts.insert(parts.begin()+i+1, rparts.begin()+1, rparts.end());
-                               i += rparts.size()-1;
-                       }
-                       else
-                               parts[i] = replace;
-               }
-       }
-}
-
-void Architecture::parse_specification(const string &spec)
-{
-       vector<string> parts = split(spec, "-");
-       resolve_aliases(parts);
-       for(const string &p: parts)
-       {
-               bool ok = false;
-
-               for(unsigned j=0; (!ok && types[j]); ++j)
-                       if(p==types[j])
-                       {
-                               if(!type.empty() && p!=type)
-                                       throw invalid_argument("Conflicting type specification");
-                               type = p;
-                               ok = true;
-                       }
-
-               for(unsigned j=0; (!ok && cpus[j]); j+=2)
-                       if(p==cpus[j])
-                       {
-                               if(type.empty())
-                                       type = cpus[j+1];
-                               else if(cpus[j+1]!=type)
-                                       throw invalid_argument("Conflicting CPU specification");
-                               cpu = p;
-                               ok = true;
-                       }
-
-               for(unsigned j=0; (!ok && fpus[j]); j+=2)
-                       if(p==fpus[j])
-                       {
-                               if(fpus[j+1]!=type)
-                                       throw invalid_argument("Conflicting FPU specification");
-                               fpu = p;
-                               ok = true;
-                       }
-
-               for(unsigned j=0; (!ok && systems[j]); ++j)
-                       if(p==systems[j])
-                       {
-                               if(!system.empty() && p!=system)
-                                       throw invalid_argument("Conflicting system specification");
-                               system = p;
-                               ok = true;
-                       }
-
-               for(unsigned j=0; (!ok && toolchains[j]); ++j)
-                       if(p==toolchains[j])
-                       {
-                               if(!toolchain.empty() && p!=toolchain)
-                                       throw invalid_argument("Conflicting toolchain specification");
-                               toolchain = p;
-                               ok = true;
-                       }
-
-               if(!ok && (p=="32" || p=="64"))
-               {
-                       unsigned b = lexical_cast<unsigned>(p);
-                       if(bits && b!=bits)
-                               throw invalid_argument("Conflicting bits specification");
-                       bits = b;
-                       ok = true;
-               }
-
-               if(!ok)
-                       throw invalid_argument("Unrecognized part in arch specification: "+p);
-       }
-}
-
-
-Architecture::Loader::Loader(Architecture &a):
-       DataFile::ObjectLoader<Architecture>(a)
-{
-       add("prefix", &Loader::cross_prefix);
-}
-
-void Architecture::Loader::cross_prefix(const string &p)
-{
-       if(!obj.native)
-               obj.cross_prefix = p;
-}
diff --git a/source/architecture.h b/source/architecture.h
deleted file mode 100644 (file)
index 2cf4fa2..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-#ifndef ARCHITECTURE_H_
-#define ARCHITECTURE_H_
-
-#include <typeinfo>
-#include <msp/datafile/loader.h>
-#include "buildinfo.h"
-#include "pattern.h"
-
-class Builder;
-
-/**
-Stores information about an architecture.  This includes CPU type, model and
-bitness and operating system.
-*/
-class Architecture
-{
-public:
-       class Loader: public Msp::DataFile::ObjectLoader<Architecture>
-       {
-       public:
-               Loader(Architecture &);
-
-       private:
-               void cross_prefix(const std::string &);
-       };
-
-private:
-       Builder &builder;
-       std::string type;
-       std::string cpu;
-       std::string fpu;
-       std::string system;
-       unsigned bits = 0;
-       std::string toolchain;
-       std::string name;
-       bool native = false;
-       std::string cross_prefix;
-       std::map<std::string, std::vector<Pattern>> filename_patterns;
-
-public:
-       Architecture(Builder &b, const std::string &spec);
-
-       void refine(const std::string &);
-private:
-       void update();
-
-public:
-       const std::string &get_type() const { return type; }
-       const std::string &get_name() const { return name; }
-       const std::string &get_system() const { return system; }
-       unsigned get_bits() const { return bits; }
-       const std::string &get_cpu() const { return cpu; }
-       const std::string &get_fpu() const { return fpu; }
-       const std::string &get_toolchain() const { return toolchain; }
-       bool match_name(const std::string &, unsigned * = 0) const;
-       std::string best_match(const std::vector<std::string> &) const;
-       bool is_native() const { return native; }
-       bool is_cross() const { return !cross_prefix.empty(); }
-
-       const std::string &get_cross_prefix() const { return cross_prefix; }
-
-       template<typename T>
-       const std::vector<Pattern> &get_patterns() const;
-
-       template<typename T>
-       std::string create_filename(const std::string &) const;
-
-private:
-       template<typename T>
-       void add_pattern(const std::string &);
-
-private:
-       static void resolve_aliases(std::vector<std::string> &);
-       void parse_specification(const std::string &);
-};
-
-template<typename T>
-inline const std::vector<Pattern> &Architecture::get_patterns() const
-{
-       auto i = filename_patterns.find(typeid(T).name());
-       if(i!=filename_patterns.end())
-               return i->second;
-
-       static std::vector<Pattern> empty;
-       return empty;
-}
-
-template<typename T>
-inline std::string Architecture::create_filename(const std::string &base) const
-{
-       const std::vector<Pattern> &patterns = get_patterns<T>();
-       return patterns.empty() ? base : patterns.front().apply(base);
-}
-
-#endif
diff --git a/source/binary.cpp b/source/binary.cpp
deleted file mode 100644 (file)
index f245037..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include <msp/strings/utils.h>
-#include "binary.h"
-#include "builder.h"
-#include "component.h"
-#include "objectfile.h"
-#include "sharedlibrary.h"
-#include "sourcepackage.h"
-#include "staticlibrary.h"
-#include "tool.h"
-
-using namespace std;
-using namespace Msp;
-
-Binary::Binary(Builder &b, const Component &c, const string &p, const vector<ObjectFile *> &objs):
-       FileTarget(b, c.get_package(), c.get_package().get_output_directory()/p),
-       objects(objs)
-{
-       component = &c;
-       for(ObjectFile *o: objects)
-               add_dependency(*o);
-
-       nested_build_sig = true;
-       arch_in_build_sig = true;
-}
-
-void Binary::collect_build_info(BuildInfo &binfo) const
-{
-       for(ObjectFile *o: objects)
-               if(const Tool *obj_tool = o->get_tool())
-                       binfo.update_from(obj_tool->get_build_info());
-
-       Target::collect_build_info(binfo);
-
-       binfo.update_from(static_binfo);
-}
-
-void Binary::find_dependencies()
-{
-       if(!component)
-               return;
-
-       vector<Target *> static_libs;
-       vector<Target *> shared_libs;
-       vector<string> missing_libs;
-       find_dependencies(this, static_libs, shared_libs, missing_libs);
-
-       for(Target *t: static_libs)
-               add_dependency(*t);
-       for(Target *t: shared_libs)
-               add_dependency(*t);
-       for(const string &m: missing_libs)
-               problems.push_back(format("Required library %s not found", m));
-}
-
-void Binary::find_dependencies(Target *tgt, vector<Target *> &static_libs, vector<Target *> &shared_libs, vector<string> &missing_libs)
-{
-       BuildInfo binfo;
-       tgt->collect_build_info(binfo);
-       if(tgt!=this)
-       {
-               static_binfo.libpath.insert(static_binfo.libpath.end(), binfo.libpath.begin(), binfo.libpath.end());
-               static_binfo.keep_symbols.insert(static_binfo.keep_symbols.end(), binfo.keep_symbols.begin(), binfo.keep_symbols.end());
-               if(binfo.threads)
-                       static_binfo.threads = true;
-       }
-
-       for(const string &l: binfo.libs)
-       {
-               if(l.size()>10 && !l.compare(l.size()-10, 10, ".framework"))
-                       continue;
-
-               BuildInfo::LibraryMode libmode = component->get_build_info().get_libmode_for(l);
-               Target *lib = builder.get_vfs().find_library(l, binfo.libpath, libmode);
-               if(lib)
-               {
-                       Target *real = lib->get_real_target();
-                       if(StaticLibrary *stlib = dynamic_cast<StaticLibrary *>(real))
-                       {
-                               /* Keep only the last occurrence of each static library.  This
-                               ensures the order is correct for linking. */
-                               auto i = find(static_libs, stlib);
-                               if(i!=static_libs.end())
-                                       static_libs.erase(i);
-                               static_libs.push_back(stlib);
-                               find_dependencies(stlib, static_libs, shared_libs, missing_libs);
-                       }
-                       else if(!any_equals(shared_libs, lib))
-                               shared_libs.push_back(lib);
-               }
-               else if(!any_equals(missing_libs, l))
-                       missing_libs.push_back(l);
-       }
-}
diff --git a/source/binary.h b/source/binary.h
deleted file mode 100644 (file)
index ac5a855..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef BINARY_H_
-#define BINARY_H_
-
-#include "buildinfo.h"
-#include "filetarget.h"
-
-class Component;
-class ObjectFile;
-
-/**
-Produces a binary file, which may be either a standalone executable or a shared
-library.
-*/
-class Binary: public FileTarget
-{
-private:
-       BuildInfo static_binfo;
-
-protected:
-       std::vector<ObjectFile *> objects;
-
-       Binary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { }
-       Binary(Builder &, const Component &, const std::string &, const std::vector<ObjectFile *> &);
-
-public:
-       void collect_build_info(BuildInfo &) const override;
-
-protected:
-       void find_dependencies() override;
-private:
-       void find_dependencies(Target *, std::vector<Target *> &, std::vector<Target *> &, std::vector<std::string> &);
-};
-
-#endif
diff --git a/source/binarycomponent.cpp b/source/binarycomponent.cpp
deleted file mode 100644 (file)
index 3209ccc..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-#include <msp/fs/utils.h>
-#include "binarycomponent.h"
-#include "builder.h"
-#include "filetarget.h"
-#include "sourcepackage.h"
-#include "tool.h"
-
-using namespace std;
-using namespace Msp;
-
-void BinaryComponent::create_build_info()
-{
-       Component::create_build_info();
-
-       for(const Component *u: uses)
-       {
-               /* Select an include path that contains all the sources for this and the
-               used component.  This should produce a sensible result in most cases. */
-               FS::Path base;
-               for(const FS::Path &s: sources)
-                       base = base.empty() ? s : FS::common_ancestor(base, s);
-               for(const FS::Path &s: u->get_sources())
-                       base = FS::common_ancestor(base, s);
-               build_info.incpath.push_back(base);
-               build_info.libs.push_back(u->get_name());
-               if(!u->get_install())
-               {
-                       build_info.libmodes[u->get_name()] = BuildInfo::STATIC;
-                       build_info.libpath.push_back(u->get_package().get_output_directory());
-               }
-       }
-
-       if(type==LIBRARY || type==MODULE)
-               if(build_info.libmode<BuildInfo::DYNAMIC)
-                       build_info.libmode = BuildInfo::DYNAMIC;
-}
-
-void BinaryComponent::update_exported_build_info(BuildInfo &binfo) const
-{
-       if(type==LIBRARY)
-               binfo.libs.push_back(name);
-}
-
-void BinaryComponent::create_targets() const
-{
-       Builder &builder = package.get_builder();
-       BuildGraph &build_graph = builder.get_build_graph();
-       const Toolchain &toolchain = builder.get_toolchain();
-       const Toolchain &pkg_tools = package.get_toolchain();
-
-       vector<Target *> objs;
-       vector<FS::Path> source_filenames = collect_source_files();
-       objs.reserve(source_filenames.size());
-       for(auto i=source_filenames.begin(); i!=source_filenames.end(); ++i)
-       {
-               string ext = FS::extpart(FS::basename(*i));
-               Target *src = 0;
-
-               Tool *gen = pkg_tools.get_tool_for_suffix(ext);
-               if(gen)
-               {
-                       vector<Target *> templates;
-                       templates.push_back(gen->create_source(*this, *i));
-
-                       Tool::ProcessingUnit processing_unit = gen->get_processing_unit();
-                       if(processing_unit!=Tool::ONE_FILE)
-                       {
-                               FS::Path source_dir = FS::dirname(*i);
-                               for(auto j=next(i); j!=source_filenames.end(); )
-                               {
-                                       if((processing_unit!=Tool::DIRECTORY || FS::dirname(*j)==source_dir) &&
-                                               pkg_tools.get_tool_for_suffix(FS::extpart(FS::basename(*j)))==gen)
-                                       {
-                                               templates.push_back(gen->create_source(*this, *j));
-                                               // Remove additional files so they won't get processed again
-                                               j = source_filenames.erase(j);
-                                       }
-                                       else
-                                               ++j;
-                               }
-                       }
-
-                       src = gen->create_target(templates);
-                       ext = FS::extpart(FS::basename(dynamic_cast<FileTarget &>(*src).get_path()));
-               }
-
-               Tool *tool = toolchain.get_tool_for_suffix(ext, true);
-               if(tool)
-               {
-                       if(!src)
-                               src = tool->create_source(*this, *i);
-                       if(!src)
-                               continue;
-
-                       if(tool->accepts_suffix(ext))
-                       {
-                               Target *obj = tool->create_target(*src);
-                               objs.push_back(obj);
-                       }
-
-                       if(type==LIBRARY && install)
-                       {
-                               if(dynamic_cast<FileTarget *>(src)->is_installable())
-                                       build_graph.add_installed_target(*src);
-
-                               for(Target *s: src->get_side_effects())
-                                       if(dynamic_cast<FileTarget *>(s)->is_installable())
-                                               build_graph.add_installed_target(*s);
-                       }
-               }
-       }
-
-       Tool &linker = toolchain.get_tool("LINK");
-
-       vector<Target *> results;
-       results.reserve(2);
-       if(type==LIBRARY)
-       {
-               Tool &archiver = toolchain.get_tool("AR");
-               results.push_back(linker.create_target(objs, "shared"));
-               results.push_back(archiver.create_target(objs));
-       }
-       else if(type==MODULE)
-               results.push_back(linker.create_target(objs, "shared"));
-       else
-               results.push_back(linker.create_target(objs));
-
-       for(Target *r: results)
-       {
-               build_graph.add_primary_target(*r);
-               if(install)
-                       build_graph.add_installed_target(*r);
-       }
-}
-
-BinaryComponent::Loader::Loader(BinaryComponent &c):
-       DataFile::DerivedObjectLoader<BinaryComponent, Component::Loader>(c)
-{
-       add("use", &Loader::use);
-}
-
-void BinaryComponent::Loader::use(const string &n)
-{
-       const BinaryComponent *comp = dynamic_cast<const BinaryComponent *>(&obj.package.get_component(n));
-       if(!comp || comp->type!=LIBRARY)
-               throw logic_error(n+" is not a library");
-
-       obj.uses.push_back(comp);
-}
diff --git a/source/binarycomponent.h b/source/binarycomponent.h
deleted file mode 100644 (file)
index ef145da..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef BINARYCOMPONENT_H_
-#define BINARYCOMPONENT_H_
-
-#include "component.h"
-
-class BinaryComponent: public Component
-{
-public:
-       class Loader: public Msp::DataFile::DerivedObjectLoader<BinaryComponent, Component::Loader>
-       {
-       public:
-               Loader(BinaryComponent &);
-       private:
-               void use(const std::string &);
-       };
-
-       enum Type
-       {
-               LIBRARY,
-               PROGRAM,
-               MODULE
-       };
-
-private:
-       Type type;
-       std::vector<const Component *> uses;
-
-public:
-       BinaryComponent(SourcePackage &p, const std::string &n, Type t): Component(p, n), type(t) { }
-
-       Type get_type() const { return type; }
-
-       void create_build_info() override;
-       void update_exported_build_info(BuildInfo &) const override;
-       void create_targets() const override;
-};
-
-#endif
diff --git a/source/binarypackage.cpp b/source/binarypackage.cpp
deleted file mode 100644 (file)
index adfca1a..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/io/print.h>
-#include <msp/strings/utils.h>
-#include "binarypackage.h"
-#include "builder.h"
-#include "filetarget.h"
-#include "staticlibrary.h"
-
-using namespace std;
-using namespace Msp;
-
-BinaryPackage::BinaryPackage(Builder &b, const string &n):
-       Package(b, n)
-{
-       use_pkgconfig = false;
-}
-
-BinaryPackage *BinaryPackage::from_flags(Builder &builder, const string &name, const Flags &flags, const Flags &static_flags)
-{
-       BinaryPackage *pkg = new BinaryPackage(builder, name);
-       pkg->use_pkgconfig = true;
-
-       process_flags(flags, pkg->export_binfo);
-
-       Flags exclusive_static_flags;
-       for(const string &f: static_flags)
-               if(!any_equals(flags, f))
-                       exclusive_static_flags.push_back(f);
-       process_flags(exclusive_static_flags, pkg->static_binfo);
-
-       return pkg;
-}
-
-void BinaryPackage::process_flags(const Flags &flags, BuildInfo &binfo)
-{
-       for(const string &f: flags)
-       {
-               if(!f.compare(0, 2, "-I"))
-                       binfo.incpath.push_back(f.substr(2));
-               else if(!f.compare(0, 2, "-D"))
-               {
-                       string::size_type equals = f.find('=');
-                       if(equals!=string::npos)
-                               binfo.defines[f.substr(2, equals-2)] = f.substr(equals+1);
-                       else
-                               binfo.defines[f.substr(2)] = string();
-               }
-               else if(!f.compare(0, 2, "-L"))
-                       binfo.libpath.push_back(f.substr(2));
-               else if(!f.compare(0, 2, "-l"))
-                       binfo.libs.push_back(f.substr(2));
-               else if(f=="-pthread")
-                       binfo.threads = true;
-       }
-}
-
-void BinaryPackage::do_prepare()
-{
-       auto is_relative = [](const FS::Path &p){ return !p.is_absolute(); };
-       bool has_relative_paths = any_of(export_binfo.libpath.begin(), export_binfo.libpath.end(), is_relative) ||
-               any_of(export_binfo.incpath.begin(), export_binfo.incpath.end(), is_relative);
-
-       vector<FS::Path> bases;
-
-       /* If we have any relative paths that need resolving, or we have no paths at
-       all and are not using pkg-config, look for files in prefix */
-       if(has_relative_paths || (!use_pkgconfig && export_binfo.libpath.empty() && export_binfo.incpath.empty()))
-               bases.push_back(builder.get_prefix());
-
-       // Always look in system locations
-       bases.push_back(FS::Path());
-
-       bool system = false;
-       for(const FS::Path &b: bases)
-       {
-               FS::Path prefix = b;
-               system = prefix.empty();
-               if(system)
-               {
-                       prefix = "/usr";
-                       const Architecture &arch = builder.get_current_arch();
-                       if(arch.is_cross())
-                               prefix /= arch.get_cross_prefix();
-               }
-
-               VirtualFileSystem::SearchPath libpath = export_binfo.libpath;
-               if(!system && libpath.empty())
-                       libpath.push_back("lib");
-               for(FS::Path &p: libpath)
-                       p = prefix/p;
-
-               bool all_found = true;
-               for(const string &l: export_binfo.libs)
-                       all_found &= (builder.get_vfs().find_library(l, libpath, export_binfo.libmode, system)!=0);
-
-               VirtualFileSystem::SearchPath incpath = export_binfo.incpath;
-               if(!system && incpath.empty())
-                       incpath.push_back("include");
-               for(FS::Path &p: incpath)
-                       p = prefix/p;
-
-               for(const string &h: headers)
-                       all_found &= (builder.get_vfs().find_header(h, 0, incpath, system)!=0);
-
-               if(all_found)
-               {
-                       base_path = prefix;
-                       builder.get_logger().log("configure", "%s found in %s", name, ((system && use_pkgconfig) ? "system" : base_path.str()));
-                       break;
-               }
-       }
-
-       if(base_path.empty())
-       {
-               // TODO report which files were not found
-               builder.get_logger().log("problems", "Cannot locate files for %s", name);
-               problems.push_back("Cannot locate files");
-               return;
-       }
-
-       /* Add default entries to paths if they're empty and the package was found
-       in a non-system location */
-       if(!system && export_binfo.incpath.empty())
-               export_binfo.incpath.push_back(base_path/"include");
-       if(!system && export_binfo.libpath.empty())
-               export_binfo.libpath.push_back(base_path/"lib");
-
-       if(has_relative_paths)
-       {
-               for(FS::Path &p: export_binfo.incpath)
-                       p = base_path/p;
-               for(FS::Path &p: export_binfo.libpath)
-                       p = base_path/p;
-       }
-
-       if(!static_binfo.libs.empty())
-       {
-               VirtualFileSystem::SearchPath combined_libpath = static_binfo.libpath;
-               combined_libpath.insert(combined_libpath.end(), export_binfo.libpath.begin(), export_binfo.libpath.end());
-
-               for(const string &l: export_binfo.libs)
-                       if(Target *lib = builder.get_vfs().find_library(l, export_binfo.libpath, BuildInfo::FORCE_STATIC, system))
-                               if(StaticLibrary *stlib = dynamic_cast<StaticLibrary *>(lib))
-                               {
-                                       for(const string &s: static_binfo.libs)
-                                               stlib->add_required_library(s);
-                                       for(const FS::Path &p: combined_libpath)
-                                               stlib->add_library_path(p);
-                               }
-       }
-}
-
-
-BinaryPackage::Loader::Loader(BinaryPackage &p):
-       DataFile::DerivedObjectLoader<BinaryPackage, Package::Loader>(p)
-{
-       add("build_info", &Loader::build_info);
-       add("header",     &Loader::header);
-}
-
-void BinaryPackage::Loader::build_info()
-{
-       load_sub(obj.export_binfo);
-}
-
-void BinaryPackage::Loader::header(const string &h)
-{
-       obj.headers.push_back(h);
-}
diff --git a/source/binarypackage.h b/source/binarypackage.h
deleted file mode 100644 (file)
index 62a8acf..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef BINARYPACKAGE_H_
-#define BINARYPACKAGE_H_
-
-#include "package.h"
-
-/**
-Represents a package that is installed on the system, but can't be built by
-Builder.
-*/
-class BinaryPackage: public Package
-{
-public:
-       class Loader: public Msp::DataFile::DerivedObjectLoader<BinaryPackage, Package::Loader>
-       {
-       public:
-               Loader(BinaryPackage &);
-       private:
-               void build_info();
-               void header(const std::string &);
-       };
-
-       using Flags = std::vector<std::string>;
-
-private:
-       Msp::FS::Path base_path;
-       std::vector<std::string> headers;
-       BuildInfo static_binfo;
-
-public:
-       BinaryPackage(Builder &, const std::string &);
-
-       const BuildInfo &get_static_build_info() const { return static_binfo; }
-
-       static BinaryPackage *from_flags(Builder &, const std::string &, const Flags &, const Flags & = Flags());
-private:
-       static void process_flags(const Flags &, BuildInfo &);
-       void do_prepare() override;
-};
-
-#endif
diff --git a/source/booleanevaluator.cpp b/source/booleanevaluator.cpp
deleted file mode 100644 (file)
index b7c399f..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-#include <stdexcept>
-#include <msp/strings/format.h>
-#include "booleanevaluator.h"
-
-using namespace std;
-using namespace Msp;
-
-BooleanEvaluator::BooleanEvaluator(const ValueFunction &f):
-       func([f](const string &value, const string *){ return f(value); }),
-       ops("&|!")
-{ }
-
-BooleanEvaluator::BooleanEvaluator(const CompareFunction &f):
-       func(f),
-       ops("&|!=^")
-{ }
-
-bool BooleanEvaluator::evaluate(const string &str)
-{
-       string buf;
-       last_was_op = true;
-       for(auto i=str.begin();; ++i)
-       {
-               if(i!=str.end() && (isalnum(*i) || (!buf.empty() && (*i=='_' || *i=='-'))))
-                       buf += *i;
-               else
-               {
-                       if(!buf.empty())
-                       {
-                               if(!last_was_op)
-                                       throw runtime_error("syntax error at "+buf);
-
-                               char op = (op_stack.empty() ? 0 : op_stack.back());
-                               if(op=='=' || op=='^')
-                               {
-                                       op_stack.pop_back();
-                                       string var = var_stack.back();
-                                       var_stack.pop_back();
-                                       bool value = (func(var, &buf) == (op=='='));
-                                       value_stack.push_back(value);
-                               }
-                               else
-                                       var_stack.push_back(buf);
-
-                               buf.clear();
-                               last_was_op = false;
-                       }
-
-                       if(i==str.end())
-                               break;
-                       else if(isspace(*i))
-                               ;
-                       else if(ops.find(*i)!=string::npos)
-                               push_op(*i);
-                       else
-                               throw runtime_error(format("syntax error at %c", *i));
-               }
-       }
-
-       collapse(0);
-
-       bool value = pop_value();
-       if(!value_stack.empty())
-               throw runtime_error("too many values");
-
-       return value;
-}
-
-void BooleanEvaluator::push_op(char op)
-{
-       if(last_was_op!=is_unary(op))
-               throw runtime_error(format("syntax error at %c", op));
-       // TODO Disallow mixing of ! and =/^
-       if(is_logic(op) && !var_stack.empty())
-               value_stack.push_back(pop_value());
-
-       if(!is_unary(op))
-               collapse(precedence(op));
-       op_stack.push_back(op);
-       last_was_op = true;
-}
-
-bool BooleanEvaluator::pop_value()
-{
-       if(!var_stack.empty())
-       {
-               string var = var_stack.back();
-               var_stack.pop_back();
-               return func(var, 0);
-       }
-       else if(!value_stack.empty())
-       {
-               bool value = value_stack.back();
-               value_stack.pop_back();
-               return value;
-       }
-
-       throw runtime_error("value stack underflow");
-}
-
-void BooleanEvaluator::collapse(unsigned until)
-{
-       while(!op_stack.empty())
-       {
-               char op = op_stack.back();
-               if(precedence(op)<until)
-                       return;
-
-               op_stack.pop_back();
-               bool value1 = pop_value();
-               if(is_unary(op))
-               {
-                       if(op=='!')
-                               value1 = !value1;
-               }
-               else
-               {
-                       bool value2 = pop_value();
-                       if(op=='&')
-                               value1 = (value1 && value2);
-                       else if(op=='|')
-                               value1 = (value1 || value2);
-               }
-               value_stack.push_back(value1);
-       }
-}
-
-unsigned BooleanEvaluator::precedence(char op)
-{
-       if(op=='&')
-               return 1;
-       else if(op=='=' || op=='^')
-               return 2;
-       else if(op=='!')
-               return 3;
-       else
-               return 0;
-}
-
-bool BooleanEvaluator::is_unary(char op)
-{
-       return op=='!';
-}
-
-bool BooleanEvaluator::is_logic(char op)
-{
-       return (op=='&' || op=='|' || op=='!');
-}
diff --git a/source/booleanevaluator.h b/source/booleanevaluator.h
deleted file mode 100644 (file)
index c3f61f2..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef BOOLEANEVALUATOR_H_
-#define BOOLEANEVALUATOR_H_
-
-#include <functional>
-#include <string>
-#include <vector>
-
-class BooleanEvaluator
-{
-public:
-       using ValueFunction = std::function<bool(const std::string &)>;
-       using CompareFunction = std::function<bool(const std::string &, const std::string *)>;
-
-private:
-       CompareFunction func;
-       std::string ops;
-       std::vector<std::string> var_stack;
-       std::vector<unsigned char> value_stack;
-       std::vector<char> op_stack;
-       bool last_was_op;
-
-public:
-       BooleanEvaluator(const ValueFunction &);
-       BooleanEvaluator(const CompareFunction &);
-
-       bool evaluate(const std::string &);
-private:
-       void push_op(char);
-       bool pop_value();
-       void collapse(unsigned);
-       unsigned precedence(char);
-       bool is_unary(char);
-       bool is_logic(char);
-};
-
-#endif
diff --git a/source/builder.cpp b/source/builder.cpp
deleted file mode 100644 (file)
index 3fc60be..0000000
+++ /dev/null
@@ -1,364 +0,0 @@
-#include <deque>
-#include <set>
-#include <msp/core/algorithm.h>
-#include <msp/core/except.h>
-#include <msp/core/maputils.h>
-#include <msp/datafile/parser.h>
-#include <msp/fs/dir.h>
-#include <msp/fs/utils.h>
-#include <msp/io/buffered.h>
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include <msp/strings/format.h>
-#include <msp/time/timedelta.h>
-#include <msp/time/utils.h>
-#include "androidtools.h"
-#include "binarypackage.h"
-#include "builder.h"
-#include "builtintools.h"
-#include "clangtools.h"
-#include "datatool.h"
-#include "gnutools.h"
-#include "installedfile.h"
-#include "microsofttools.h"
-#include "package.h"
-#include "sharedlibrary.h"
-#include "sourcepackage.h"
-#include "task.h"
-#include "virtualtarget.h"
-
-using namespace std;
-using namespace Msp;
-
-Builder::Builder():
-       package_manager(*this),
-       native_arch(*this, string()),
-       vfs(*this),
-       build_graph(*this),
-       logger(&default_logger)
-{
-       set_architecture(string());
-}
-
-Builder::~Builder()
-{
-       if(current_arch!=&native_arch)
-               delete current_arch;
-}
-
-void Builder::set_architecture(const string &name)
-{
-       if(current_arch!=&native_arch)
-               delete current_arch;
-
-       if(name.empty())
-               current_arch = &native_arch;
-       else
-               current_arch = new Architecture(*this, name);
-
-       if(auto_prefix)
-               update_auto_prefix();
-}
-
-vector<string> Builder::get_build_types() const
-{
-       vector<string> keys;
-       keys.reserve(build_types.size());
-       for(const auto &kvp: build_types)
-               keys.push_back(kvp.first);
-       return keys;
-}
-
-const BuildType &Builder::get_build_type() const
-{
-       if(!build_type)
-               throw invalid_state("no build type");
-       return *build_type;
-}
-
-void Builder::set_build_type(const string &name)
-{
-       build_type = &get_item(build_types, name);
-}
-
-void Builder::set_prefix(const FS::Path &p)
-{
-       auto_prefix = false;
-       prefix = p;
-}
-
-void Builder::set_temp_directory(const FS::Path &p)
-{
-       tempdir = p;
-}
-
-void Builder::update_auto_prefix()
-{
-       if(current_arch->is_native())
-               prefix = FS::get_home_dir()/"local";
-       else
-               prefix = FS::get_home_dir()/"local"/current_arch->get_name();
-}
-
-void Builder::add_default_tools()
-{
-       toolchain.add_toolchain(new GnuTools(*this, *current_arch));
-       toolchain.add_toolchain(new ClangTools(*this, *current_arch));
-       if(current_arch->get_system()=="android")
-               toolchain.add_toolchain(new AndroidTools(*this, *current_arch));
-       if(current_arch->get_system()=="windows")
-               toolchain.add_toolchain(new MicrosoftTools(*this, *current_arch));
-       toolchain.add_toolchain(new BuiltinTools(*this));
-       toolchain.add_tool(new DataTool(*this));
-
-       auto i = find_if(toolchain.get_toolchains(), [](const Toolchain *tc){ return (tc->has_tool("CC") || tc->has_tool("CXX")); });
-       if(i!=toolchain.get_toolchains().end())
-       {
-               current_arch->refine((*i)->get_name());
-               if(auto_prefix)
-                       update_auto_prefix();
-       }
-}
-
-void Builder::set_logger(const Logger *l)
-{
-       logger = (l ? l : &default_logger);
-}
-
-vector<string> Builder::collect_problems() const
-{
-       vector<string> problems;
-       set<const Package *> broken_packages;
-       set<const Component *> broken_components;
-       set<const Tool *> broken_tools;
-
-       for(const auto &kvp: build_graph.get_targets())
-               if(kvp.second->is_broken())
-               {
-                       for(const string &p: kvp.second->get_problems())
-                               problems.push_back(format("%s: %s", kvp.second->get_name(), p));
-
-                       const Package *package = kvp.second->get_package();
-                       if(package && !package->get_problems().empty())
-                               broken_packages.insert(package);
-
-                       const Component *component = kvp.second->get_component();
-                       if(component && !component->get_problems().empty())
-                               broken_components.insert(component);
-
-                       const Tool *tool = kvp.second->get_tool();
-                       if(tool && !tool->get_problems().empty())
-                               broken_tools.insert(tool);
-               }
-
-       // TODO Sort components after their packages, and targets last
-       for(const Package *p: broken_packages)
-               for(const string &b: p->get_problems())
-                       problems.push_back(format("%s: %s", p->get_name(), b));
-
-       for(const Component *c: broken_components)
-               for(const string &b: c->get_problems())
-                       problems.push_back(format("%s/%s: %s", c->get_package().get_name(), c->get_name(), b));
-
-       for(const Tool *t: broken_tools)
-               for(const string &b: t->get_problems())
-                       problems.push_back(format("%s: %s", t->get_tag(), b));
-
-       return problems;
-}
-
-void Builder::load_build_file(const FS::Path &fn, const Config::InputOptions *opts, bool all)
-{
-       IO::BufferedFile in(fn.str());
-
-       get_logger().log("files", "Reading %s", fn);
-
-       DataFile::Parser parser(in, fn.str());
-       Loader loader(*this, opts, all);
-       loader.load(parser);
-}
-
-void Builder::save_caches()
-{
-       for(const auto &kvp: package_manager.get_packages())
-               kvp.second->save_caches();
-}
-
-int Builder::build(unsigned jobs, bool dry_run, bool show_progress)
-{
-       unsigned total = build_graph.count_rebuild_targets();
-
-       if(!total)
-       {
-               get_logger().log("summary", "Already up to date");
-               return 0;
-       }
-       get_logger().log("summary", "Will build %d target%s", total, (total!=1 ? "s" : ""));
-
-       vector<Task *> tasks;
-
-       unsigned count = 0;
-
-       bool fail = false;
-       bool finish = false;
-       bool starved = false;
-
-       while(!finish)
-       {
-               if(tasks.size()<jobs && !fail && !starved)
-               {
-                       Target *tgt = build_graph.get_buildable_target();
-                       if(tgt)
-                       {
-                               if(tgt->get_tool())
-                               {
-                                       if(show_progress)
-                                               IO::print("\033[K");
-                                       get_logger().log("tasks", "%-4s  %s", tgt->get_tool()->get_tag(), tgt->get_name());
-                               }
-                               Task *task = tgt->build();
-                               if(task)
-                               {
-                                       get_logger().log("commands", "%s", task->get_command());
-                                       if(dry_run)
-                                       {
-                                               task->signal_finished.emit(true);
-                                               delete task;
-                                       }
-                                       else
-                                       {
-                                               task->start();
-                                               tasks.push_back(task);
-                                       }
-                               }
-
-                               if(show_progress)
-                                       IO::print("%d of %d target%s built\033[1G", count, total, (total!=1 ? "s" : ""));
-                       }
-                       else if(tasks.empty())
-                               finish = true;
-                       else
-                               starved = true;
-               }
-               else
-                       Time::sleep(10*Time::msec);
-
-               for(unsigned i=0; i<tasks.size();)
-               {
-                       Task::Status status;
-                       if(jobs==1 || (tasks.size()==1 && starved))
-                               status = tasks[i]->wait();
-                       else
-                               status = tasks[i]->check();
-
-                       if(status!=Task::RUNNING)
-                       {
-                               ++count;
-
-                               delete tasks[i];
-                               tasks.erase(tasks.begin()+i);
-                               if(status==Task::ERROR)
-                                       fail = true;
-                               if(tasks.empty() && fail)
-                                       finish = true;
-                               starved = false;
-                       }
-                       else
-                               ++i;
-               }
-       }
-
-       if(show_progress)
-               IO::print("\033[K");
-       if(fail)
-               get_logger().log("summary", "Build failed");
-       else if(show_progress)
-               get_logger().log("summary", "Build complete");
-
-       return fail;
-}
-
-int Builder::clean(bool all, bool dry_run)
-{
-       // Cleaning doesn't care about ordering, so a simpler method can be used
-
-       set<Target *> clean_tgts;
-       deque<Target *> queue;
-       queue.push_back(&build_graph.get_goals());
-
-       while(!queue.empty())
-       {
-               Target *tgt = queue.front();
-               queue.pop_front();
-
-               if(tgt->is_buildable() && (tgt->get_package()==&package_manager.get_main_package() || all))
-                       clean_tgts.insert(tgt);
-
-               for(Target *t: tgt->get_dependencies())
-                       if(!clean_tgts.count(t))
-                               queue.push_back(t);
-       }
-
-       for(Target *t: clean_tgts)
-       {
-               get_logger().log("tasks", "RM    %s", t->get_name());
-               if(!dry_run)
-                       t->clean();
-       }
-
-       return 0;
-}
-
-
-Builder::Loader::Loader(Builder &b, const Config::InputOptions *o, bool a):
-       DataFile::ObjectLoader<Builder>(b),
-       options(o),
-       conf_all(a)
-{
-       add("architecture", &Loader::architecture);
-       add("binary_package", &Loader::binpkg);
-       add("build_type", &Loader::build_type);
-       add("package", &Loader::package);
-
-       if(!obj.top_loader)
-               obj.top_loader = this;
-       else if(!options && obj.top_loader!=this && obj.top_loader->conf_all)
-               options = obj.top_loader->options;
-}
-
-Builder::Loader::~Loader()
-{
-       if(obj.top_loader==this)
-               obj.top_loader = 0;
-}
-
-void Builder::Loader::architecture(const string &n)
-{
-       if(obj.current_arch->match_name(n))
-               load_sub(*obj.current_arch);
-}
-
-void Builder::Loader::binpkg(const string &n)
-{
-       BinaryPackage *pkg = new BinaryPackage(obj, n);
-       load_sub(*pkg);
-}
-
-void Builder::Loader::build_type(const string &n)
-{
-       BuildType btype(n);
-       load_sub(btype);
-       auto i = obj.build_types.insert({ n, btype }).first;
-       if(!obj.build_type)
-               obj.build_type = &i->second;
-}
-
-void Builder::Loader::package(const string &n)
-{
-       SourcePackage *pkg = new SourcePackage(obj, n, get_source());
-
-       load_sub(*pkg, options);
-
-       if(obj.build_type)
-               pkg->set_build_type(*obj.build_type);
-}
diff --git a/source/builder.h b/source/builder.h
deleted file mode 100644 (file)
index e06cec3..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-#ifndef BUILDER_H_
-#define BUILDER_H_
-
-#include <map>
-#include <string>
-#include <msp/datafile/loader.h>
-#include <msp/fs/path.h>
-#include "architecture.h"
-#include "buildgraph.h"
-#include "buildtype.h"
-#include "config.h"
-#include "logger.h"
-#include "packagemanager.h"
-#include "target.h"
-#include "toolchain.h"
-#include "virtualfilesystem.h"
-
-class FileTarget;
-class Package;
-class SourcePackage;
-
-/**
-This class ties everything else together.  It also contains code for loading
-build files and supervising the build process.
-*/
-class Builder
-{
-private:
-       class Loader: public Msp::DataFile::ObjectLoader<Builder>
-       {
-       private:
-               const Config::InputOptions *options;
-               bool conf_all;
-
-       public:
-               Loader(Builder &, const Config::InputOptions * = 0, bool = false);
-               ~Loader();
-
-       private:
-               void architecture(const std::string &);
-               void binpkg(const std::string &);
-               void build_type(const std::string &);
-               void package(const std::string &);
-       };
-
-private:
-       PackageManager package_manager;
-
-       Architecture native_arch;
-       Architecture *current_arch = 0;
-       std::map<std::string, BuildType> build_types;
-       BuildType *build_type = 0;
-       Toolchain toolchain;
-       VirtualFileSystem vfs;
-       BuildGraph build_graph;
-       Logger default_logger;
-       const Logger *logger;
-
-       bool auto_prefix = true;
-       Msp::FS::Path prefix;
-       Msp::FS::Path tempdir = "temp";
-
-       Loader *top_loader = 0;
-
-public:
-       Builder();
-       ~Builder();
-
-       PackageManager &get_package_manager() { return package_manager; }
-
-       void set_architecture(const std::string &);
-       const Architecture &get_current_arch() const { return *current_arch; }
-       const Architecture &get_native_arch() const { return native_arch; }
-       void set_build_type(const std::string &);
-       std::vector<std::string> get_build_types() const;
-       const BuildType &get_build_type() const;
-       BuildGraph &get_build_graph() { return build_graph; }
-       void set_prefix(const Msp::FS::Path &);
-       void set_temp_directory(const Msp::FS::Path &);
-       const Msp::FS::Path &get_prefix() const { return prefix; }
-       const Msp::FS::Path &get_temp_directory() const { return tempdir; }
-
-private:
-       void update_auto_prefix();
-
-public:
-       void add_default_tools();
-       const Toolchain &get_toolchain() const { return toolchain; }
-       VirtualFileSystem &get_vfs() { return vfs; }
-       void set_logger(const Logger *);
-       const Logger &get_logger() const { return *logger; }
-
-       std::vector<std::string> collect_problems() const;
-
-       /** Loads a build file.  If opts is not null, it is used to configure any
-       packages loaded from this file.  If all is true, external packages are also
-       configured. */
-       void load_build_file(const Msp::FS::Path &, const Config::InputOptions *opts = 0, bool all = false);
-
-       /** Saves package configuration and dependency caches. */
-       void save_caches();
-
-       /** Builds the goal targets.  The build graph must be prepared first. */
-       int build(unsigned jobs = 1, bool dry_run = false, bool show_progress = false);
-
-       /** Cleans buildable targets.  If all is true, cleans all packages.
-       Otherwise cleans only the default package. */
-       int clean(bool all = false, bool dry_run = false);
-
-       int do_create_makefile();
-};
-
-#endif
diff --git a/source/buildercli.cpp b/source/buildercli.cpp
deleted file mode 100644 (file)
index bc18eed..0000000
+++ /dev/null
@@ -1,374 +0,0 @@
-#include <msp/core/getopt.h>
-#include <msp/fs/dir.h>
-#include <msp/fs/stat.h>
-#include <msp/io/print.h>
-#include <msp/strings/format.h>
-#include <msp/strings/utils.h>
-#include "analyzer.h"
-#include "buildercli.h"
-#include "filetarget.h"
-#include "sourcepackage.h"
-#include "tool.h"
-#include "toolchain.h"
-
-using namespace std;
-using namespace Msp;
-
-BuilderCLI::BuilderCLI(int argc, char **argv):
-       RegisteredApplication<BuilderCLI>("builder")
-{
-       string analyze_mode;
-       string work_dir;
-       bool full_paths = false;
-       unsigned max_depth = 4;
-       string prefix;
-       string tempdir;
-       string arch;
-       bool no_externals = false;
-       unsigned verbose = 1;
-       bool silent = false;
-       vector<string> log_channels;
-       string build_type;
-
-       GetOpt getopt;
-       getopt.add_option('a', "analyze",    analyze_mode,  GetOpt::REQUIRED_ARG).set_help("Perform dependency analysis.", "MODE");
-       getopt.add_option('b', "build",      build,         GetOpt::NO_ARG).set_help("Perform build even if also doing something else.");
-       getopt.add_option('c', "clean",      clean,         GetOpt::NO_ARG).set_help("Clean buildable targets.");
-       getopt.add_option('f', "file",       build_file,    GetOpt::REQUIRED_ARG).set_help("Read build instructions from FILE.", "FILE");
-       getopt.add_option('h', "help",       help,          GetOpt::NO_ARG).set_help("Print this message.");
-       getopt.add_option('j', "jobs",       jobs,          GetOpt::REQUIRED_ARG).set_help("Run up to NUM tasks in parallel.", "NUM");
-       getopt.add_option('l', "log",        log_channels,  GetOpt::REQUIRED_ARG).set_help("Enable listed log channels.", "LIST");
-       getopt.add_option('n', "dry-run",    dry_run,       GetOpt::NO_ARG).set_help("Show what would be done without actually doing it.");
-       getopt.add_option('s', "silent",     silent,        GetOpt::NO_ARG).set_help("Don't print any messages other than errors.");
-       getopt.add_option('t', "build-type", build_type,    GetOpt::REQUIRED_ARG).set_help("Set build type.", "TYPE");
-       getopt.add_option('v', "verbose",    verbose,       GetOpt::NO_ARG).set_help("Print more information about what's going on.");
-       getopt.add_option('x', "no-externals",no_externals, GetOpt::NO_ARG).set_help("Do not load external source packages.");
-       getopt.add_option('A', "conf-all",   conf_all,      GetOpt::NO_ARG).set_help("Apply configuration to all packages.");
-       getopt.add_option('B', "build-all",  build_all,     GetOpt::NO_ARG).set_help("Build all targets unconditionally.");
-       getopt.add_option('C', "chdir",      work_dir,      GetOpt::REQUIRED_ARG).set_help("Change to DIR before doing anything else.", "DIR");
-       getopt.add_option('P', "progress",   show_progress, GetOpt::NO_ARG).set_help("Display progress while building.");
-       getopt.add_option('W', "what-if",    what_if,       GetOpt::REQUIRED_ARG).set_help("Pretend that FILE has changed.", "FILE");
-       getopt.add_option(     "arch",       arch,          GetOpt::REQUIRED_ARG).set_help("Build for architecture ARCH.", "ARCH");
-       getopt.add_option(     "conf-only",  conf_only,     GetOpt::NO_ARG).set_help("Stop after configuring packages.");
-       getopt.add_option(     "full-paths", full_paths,    GetOpt::NO_ARG).set_help("Output full paths in analysis.");
-       getopt.add_option(     "max-depth",  max_depth,     GetOpt::REQUIRED_ARG).set_help("Show up to NUM levels in analysis.", "NUM");
-       getopt.add_option(     "prefix",     prefix,        GetOpt::REQUIRED_ARG).set_help("Install things to DIR.", "DIR");
-       getopt.add_option(     "tempdir",    tempdir,       GetOpt::REQUIRED_ARG).set_help("Store temporary files in DIR.", "DIR");
-       getopt.add_argument("target", cmdline_targets, GetOpt::OPTIONAL_ARG).set_help("Target(s) to build");
-       getopt(argc, argv);
-
-       if(help)
-       {
-               helpmsg = "Usage:\n  ";
-               helpmsg += getopt.generate_usage(argv[0], true);
-               helpmsg += "\n\n";
-               helpmsg += getopt.generate_help();
-       }
-
-       if(silent)
-               --verbose;
-       if(verbose>=1)
-       {
-               logger.enable_channel("summary");
-               logger.enable_channel("tasks");
-       }
-       if(verbose>=2)
-       {
-               logger.enable_channel("environment");
-               logger.enable_channel("packages");
-               logger.enable_channel("commands");
-       }
-       if(verbose>=3)
-       {
-               logger.enable_channel("files");
-               logger.enable_channel("auxcommands");
-       }
-       for(const string &c: log_channels)
-               for(const string &p: split(c, ','))
-                       logger.enable_channel(p);
-       builder.set_logger(&logger);
-
-       if(!analyze_mode.empty())
-       {
-               analyzer = new Analyzer(builder);
-
-               if(analyze_mode=="deps")
-                       analyzer->set_mode(Analyzer::DEPS);
-               else if(analyze_mode=="alldeps")
-                       analyzer->set_mode(Analyzer::ALLDEPS);
-               else if(analyze_mode=="rebuild")
-                       analyzer->set_mode(Analyzer::REBUILD);
-               else if(analyze_mode=="rdeps")
-                       analyzer->set_mode(Analyzer::RDEPS);
-               else
-                       throw usage_error("Invalid analyze mode");
-
-               analyzer->set_max_depth(max_depth);
-               analyzer->set_full_paths(full_paths);
-       }
-       else if(!clean && !create_makefile)
-               build = true;
-
-       if(!work_dir.empty())
-               FS::chdir(work_dir);
-
-       cwd = FS::getcwd();
-
-       PackageManager &package_manager = builder.get_package_manager();
-
-       package_manager.append_package_path(cwd);
-       package_manager.append_package_path(cwd/"..");
-       package_manager.append_binary_package_path(FS::get_sys_data_dir()/"packages");
-
-       package_manager.set_no_externals(no_externals);
-
-       builder.set_architecture(tolower(arch));
-
-       vector<FS::Path> start_files;
-       start_files.push_back(FS::get_sys_data_dir()/"builderrc");
-       start_files.push_back(FS::get_user_data_dir()/"rc");
-       for(const FS::Path &f: start_files)
-               if(FS::exists(f))
-                       builder.load_build_file(f);
-
-       if(!prefix.empty())
-               builder.set_prefix(cwd/prefix);
-
-       if(!tempdir.empty())
-               builder.set_temp_directory(tempdir);
-
-       if(!build_type.empty())
-               builder.set_build_type(build_type);
-
-       builder.add_default_tools();
-
-       const Toolchain &toolchain = builder.get_toolchain();
-       for(auto i=cmdline_targets.begin(); i!=cmdline_targets.end(); )
-       {
-               string::size_type equal = i->find('=');
-               if(equal!=string::npos)
-               {
-                       string key = i->substr(0, equal);
-                       string value = i->substr(equal+1);
-                       if(toolchain.has_tool(key))
-                               toolchain.get_tool(key).set_command(value);
-                       else
-                               cmdline_options.insert({ key, value });
-                       i = cmdline_targets.erase(i);
-               }
-               else
-                       ++i;
-       }
-}
-
-BuilderCLI::~BuilderCLI()
-{
-       delete analyzer;
-}
-
-int BuilderCLI::main()
-{
-       FS::Path main_file = cwd/build_file;
-       if(FS::exists(main_file))
-       {
-               builder.load_build_file(main_file, &cmdline_options, conf_all);
-               if(!dry_run && !cmdline_options.empty())
-                       builder.save_caches();
-       }
-       else if(!help)
-       {
-               IO::print(IO::cerr, "The file %s does not exist.\n", main_file);
-               return 1;
-       }
-
-       if(help)
-       {
-               IO::print("Builder 3.0\n"
-                       "Copyright Â© 2006-2022  Mikkosoft Productions, Mikko Rasa\n"
-                       "Licensed under the GPL\n\n"
-                       "%s", helpmsg);
-               package_help();
-               return 0;
-       }
-
-       const Architecture &native_arch = builder.get_native_arch();
-       const Architecture &current_arch = builder.get_current_arch();
-       logger.log("environment", "Building on %s, for %s%s", native_arch.get_name(),
-               current_arch.get_name(), (current_arch.is_native() ? " (native)" : ""));
-       logger.log("environment", "Prefix is %s", builder.get_prefix());
-       const FS::Path &tempdir = builder.get_temp_directory();
-       if(tempdir.is_absolute())
-               logger.log("environment", "Temporary directory is %s", tempdir);
-       else
-               logger.log("environment", "Using per-package temporary directory %s", tempdir);
-       const BuildType &build_type = builder.get_build_type();
-       logger.log("environment", "Build type is %s", build_type.get_name());
-
-       if(!prepare_build())
-               return 1;
-
-       if(conf_only)
-               return 0;
-
-       BuildGraph &build_graph = builder.get_build_graph();
-       PackageManager &package_manager = builder.get_package_manager();
-       vector<string> package_details;
-       for(const auto &kvp: package_manager.get_packages())
-       {
-               if(!kvp.second->is_prepared())
-                       continue;
-
-               string line = kvp.second->get_name();
-               if(dynamic_cast<SourcePackage *>(kvp.second))
-               {
-                       line += '*';
-
-                       unsigned count = 0;
-                       unsigned to_be_built = 0;
-                       for(const auto &kvp2: build_graph.get_targets())
-                               if(kvp2.second->get_package()==kvp.second)
-                               {
-                                       ++count;
-                                       if(kvp2.second->needs_rebuild())
-                                               ++to_be_built;
-                               }
-                       if(count)
-                       {
-                               line += format(" (%d targets", count);
-                               if(to_be_built)
-                                       line += format(", %d to be built", to_be_built);
-                               line += ')';
-                       }
-               }
-
-               package_details.push_back(line);
-       }
-
-       logger.log("summary", "%d active packages, %d targets", package_details.size(), build_graph.get_targets().size());
-       for(const string &d: package_details)
-               logger.log("packages", d);
-
-       if(analyzer)
-               analyzer->analyze();
-
-       if(build_graph.get_goals().is_broken())
-       {
-               vector<string> problems = builder.collect_problems();
-               IO::print(IO::cerr, "The following problems were detected:\n");
-               for(const string &p: problems)
-                       IO::print(IO::cerr, "  %s\n", p);
-               if(!analyzer)
-                       IO::print(IO::cerr, "Please fix them and try again.\n");
-               return 1;
-       }
-
-       if(clean)
-               exit_code = builder.clean(clean>=2, dry_run);
-       if(build)
-               exit_code = builder.build(jobs, dry_run, show_progress);
-
-       if(!dry_run)
-               builder.save_caches();
-
-       return exit_code;
-}
-
-bool BuilderCLI::prepare_build()
-{
-       /* XXX This is ugly; using the Builder class should not be this convoluted.
-       But the command line targets and what ifs need to be set at certain points
-       during preparation. */
-       BuildGraph &build_graph = builder.get_build_graph();
-       PackageManager &package_manager = builder.get_package_manager();
-
-       package_manager.get_main_package().prepare();
-
-       // Add targets from command line as goals
-       for(const string &t: cmdline_targets)
-       {
-               Target *tgt = resolve_target(t);
-               if(!tgt)
-               {
-                       IO::print("I don't know anything about %s\n", t);
-                       return false;
-               }
-
-               build_graph.add_goal(*tgt);
-       }
-
-       build_graph.prepare();
-
-       // Apply what-ifs
-       for(const string &w: what_if)
-       {
-               FileTarget *tgt = dynamic_cast<FileTarget *>(resolve_target(w));
-               if(!tgt)
-               {
-                       IO::print(IO::cerr, "Unknown what-if target %s\n", w);
-                       return false;
-               }
-               tgt->touch();
-       }
-
-       if(build_all)
-               build_graph.force_full_rebuild();
-
-       if(!dry_run)
-               package_manager.save_all_caches();
-
-       return true;
-}
-
-Target *BuilderCLI::resolve_target(const string &name)
-{
-       Target *tgt = builder.get_build_graph().get_target(name);
-       if(!tgt)
-               tgt = builder.get_vfs().get_target(cwd/name);
-       return tgt;
-}
-
-void BuilderCLI::package_help()
-{
-       PackageManager &package_manager = builder.get_package_manager();
-       if(package_manager.get_packages().empty())
-               return;
-
-       SourcePackage &main_pkg = dynamic_cast<SourcePackage &>(package_manager.get_main_package());
-       const Config &config = main_pkg.get_config();
-       const auto &options = config.get_options();
-       const Package::Requirements &requires = main_pkg.get_required_packages();
-
-       if(!requires.empty() || !options.empty())
-               IO::print("\nHelp for package %s:\n", main_pkg.get_name());
-
-       if(!requires.empty())
-       {
-               IO::print("\nRequired packages:\n  ");
-               for(auto i=requires.begin(); i!=requires.end(); ++i)
-               {
-                       if(i!=requires.begin())
-                               IO::print(", ");
-                       IO::print((*i)->get_name());
-               }
-               IO::print("\n");
-       }
-
-       if(!options.empty())
-       {
-               IO::print("\nPackage configuration:\n");
-               for(const auto &kvp: options)
-               {
-                       const Config::Option &opt = kvp.second;
-                       string line = format("  %s: %s (%s)", opt.name, opt.description, opt.value);
-                       if(!opt.choices.empty())
-                       {
-                               line += " {";
-                               line += join(opt.choices.begin(), opt.choices.end(), " ");
-                               line += '}';
-                       }
-                       else if(opt.value!=opt.default_value)
-                               line += format(" [%s]", opt.default_value);
-                       IO::print("%s\n", line);
-               }
-       }
-}
diff --git a/source/buildercli.h b/source/buildercli.h
deleted file mode 100644 (file)
index 3b15541..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef BUILDERCLI_H_
-#define BUILDERCLI_H_
-
-#include <msp/core/application.h>
-#include "builder.h"
-
-class Analyzer;
-
-/**
-Provides a command-line interface for Builder.
-*/
-class BuilderCLI: public Msp::RegisteredApplication<BuilderCLI>
-{
-private:
-       std::vector<std::string> cmdline_targets;
-       Config::InputOptions cmdline_options;
-       Msp::FS::Path cwd;
-
-       Builder builder;
-       Logger logger;
-       Analyzer *analyzer = 0;
-       bool build = false;
-       unsigned clean = 0;
-       bool dry_run = false;
-       bool help = false;
-       std::string helpmsg;
-       bool show_progress = false;
-       std::string build_file = "Build";
-       unsigned jobs = 1;
-       std::vector<std::string> what_if;
-       bool conf_all = false;
-       bool conf_only = false;
-       bool build_all = false;
-       bool create_makefile = false;
-
-public:
-       BuilderCLI(int, char **);
-       ~BuilderCLI();
-
-       int main() override;
-
-private:
-       bool prepare_build();
-       Target *resolve_target(const std::string &);
-
-       void package_help();
-};
-
-#endif
diff --git a/source/buildgraph.cpp b/source/buildgraph.cpp
deleted file mode 100644 (file)
index 070f934..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#include "builder.h"
-#include "buildgraph.h"
-#include "component.h"
-#include "sourcepackage.h"
-#include "tool.h"
-#include "virtualtarget.h"
-
-using namespace std;
-
-BuildGraph::BuildGraph(Builder &b):
-       builder(b),
-       goals(new VirtualTarget(builder, "goals"))
-{
-       Target *world = new VirtualTarget(builder, "world");
-       world->add_dependency(*new VirtualTarget(builder, "default"));
-       world->add_dependency(*new VirtualTarget(builder, "install"));
-       world->add_dependency(*new VirtualTarget(builder, "archives"));
-}
-
-BuildGraph::~BuildGraph()
-{
-       for(const auto &kvp: targets)
-               delete kvp.second;
-}
-
-Target *BuildGraph::get_target(const string &n) const
-{
-       auto i = targets.find(n);
-       if(i!=targets.end())
-               return i->second;
-       return 0;
-}
-
-void BuildGraph::add_target(Target *t)
-{
-       targets.insert({ t->get_name(), t });
-}
-
-void BuildGraph::add_primary_target(Target &t)
-{
-       get_target("world")->add_dependency(t);
-
-       Package *main_pkg = &builder.get_package_manager().get_main_package();
-       if(t.get_package()==main_pkg && t.get_component() && t.get_component()->is_default())
-               get_target("default")->add_dependency(t);
-}
-
-void BuildGraph::add_installed_target(Target &t)
-{
-       Target *inst_tgt = 0;
-       if(const Tool *tool = t.get_tool())
-               inst_tgt = tool->create_install(t);
-       if(!inst_tgt)
-               inst_tgt = builder.get_toolchain().get_tool("CP").create_target(t);
-       get_target("install")->add_dependency(*inst_tgt);
-}
-
-void BuildGraph::add_goal(Target &t)
-{
-       goals->add_dependency(t);
-}
-
-void BuildGraph::prepare()
-{
-       if(goals->get_dependencies().empty())
-               add_goal(*get_target("default"));
-       goals->prepare();
-}
-
-void BuildGraph::force_full_rebuild()
-{
-       for(const auto &kvp: targets)
-               if(kvp.second->is_buildable() && !kvp.second->needs_rebuild())
-                       kvp.second->force_rebuild();
-}
-
-unsigned BuildGraph::count_rebuild_targets() const
-{
-       unsigned count = 0;
-       for(const auto &kvp: targets)
-               if(kvp.second->is_buildable() && kvp.second->needs_rebuild())
-                       ++count;
-       return count;
-}
-
-Target *BuildGraph::get_buildable_target() const
-{
-       return goals->get_buildable_target();
-}
diff --git a/source/buildgraph.h b/source/buildgraph.h
deleted file mode 100644 (file)
index 371b544..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef BUILDGRAPH_H_
-#define BUILDGRAPH_H_
-
-#include <map>
-#include <string>
-
-class Builder;
-class Target;
-
-/**
-Manages a graph of targets.
-*/
-class BuildGraph
-{
-private:
-       Builder &builder;
-       std::map<std::string, Target *> targets;
-       Target *goals;
-
-public:
-       BuildGraph(Builder &);
-       ~BuildGraph();
-
-       /** Looks up a target by name.  Returns 0 if no such target exists. */
-       Target *get_target(const std::string &) const;
-
-       const std::map<std::string, Target *> &get_targets() const { return targets; }
-
-       /** Adds a target.  It can later be retrieved by name.  Called from Target
-       constructor. */
-       void add_target(Target *);
-
-       /** Adds a target that is a primary build goal.  Such targets will be added
-       as dependencies of the "world" virtual target.  If the target belongs to a
-       default component of the main package, it's also added to the "default"
-       virtual target. */
-       void add_primary_target(Target &);
-
-       /** Adds a target that will be installed.  A new InstalledFile target is
-       created and added as a dependency to the "install" virtual target. */
-       void add_installed_target(Target &);
-
-       /** Adds a target as a toplevel goal.  These are stored as dependencies of
-       the "goals" virtual target. */
-       void add_goal(Target &);
-
-       Target &get_goals() const { return *goals; }
-
-       /** Prepares all toplevel goals for building.  If no goals are defined, the
-       "default" target is added as a goal. */
-       void prepare();
-
-       /** Marks all buildable targets to be rebuilt.  The graph must be prepared
-       first. */
-       void force_full_rebuild();
-
-       /** Returns the number of targets that are going to be rebuilt.  The graph
-       must be prepared first. */
-       unsigned count_rebuild_targets() const;
-
-       /** Returns a target that can be built and is needed for building the goal
-       targets.  Null */
-       Target *get_buildable_target() const;
-};
-
-#endif
diff --git a/source/buildinfo.cpp b/source/buildinfo.cpp
deleted file mode 100644 (file)
index 8e11670..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/strings/format.h>
-#include "buildinfo.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace {
-
-/** Removes any duplicate entries from a vector, leaving only the first one.
-The order of other elements is preserved. */
-template<typename T>
-void unique(vector<T> &v)
-{
-       vector<T> seen;
-       for(auto i=v.begin(); i!=v.end(); )
-       {
-               auto j = lower_bound(seen, *i);
-               if(j!=seen.end() && *j==*i)
-                       i = v.erase(i);
-               else
-                       seen.insert(j, *i++);
-       }
-}
-
-}
-
-
-BuildInfo::LibraryMode BuildInfo::get_libmode_for(const string &lib) const
-{
-       auto i = libmodes.find(lib);
-       if(i!=libmodes.end())
-               return i->second;
-       return libmode;
-}
-
-void BuildInfo::update_from(const BuildInfo &bi, UpdateLevel level)
-{
-       for(const auto &kvp: bi.defines)
-               defines[kvp.first] = kvp.second;
-       incpath.insert(incpath.begin(), bi.incpath.begin(), bi.incpath.end());
-       threads = bi.threads;
-
-       for(const auto &kvp: bi.standards)
-       {
-               auto j = standards.find(kvp.first);
-               if(j==standards.end())
-                       standards.insert(kvp);
-               else if(kvp.second.type!=j->second.type || kvp.second.year!=j->second.year)
-               {
-                       if(!kvp.second.type.compare(0, 3, "gnu"))
-                               j->second.type = kvp.second.type;
-                       if(kvp.second.year>j->second.year)
-                               j->second.year = kvp.second.year;
-               }
-       }
-
-       if(level!=CHAINED)
-       {
-               libpath.insert(libpath.begin(), bi.libpath.begin(), bi.libpath.end());
-               libs.insert(libs.begin(), bi.libs.begin(), bi.libs.end());
-       }
-
-       if(level==LOCAL)
-       {
-               sysroot = bi.sysroot;
-               local_incpath.insert(local_incpath.begin(), bi.local_incpath.begin(), bi.local_incpath.end());
-               libmode = bi.libmode;
-               rpath_mode = bi.rpath_mode;
-               for(const auto &kvp: bi.libmodes)
-                       libmodes[kvp.first] = kvp.second;
-               keep_symbols.insert(keep_symbols.end(), bi.keep_symbols.begin(), bi.keep_symbols.end());
-               debug = bi.debug;
-               optimize = bi.optimize;
-               strip = bi.strip;
-               warning_level = bi.warning_level;
-               fatal_warnings = bi.fatal_warnings;
-       }
-
-       unique(incpath);
-       unique(local_incpath);
-       unique(libpath);
-       unique(libs);
-       unique(keep_symbols);
-}
-
-
-BuildInfo::LanguageStandard::LanguageStandard(const string &std)
-{
-       auto i = find_if(std, [](char c){ return isdigit(static_cast<unsigned char>(c)); });
-       string::size_type num = i-std.begin();
-       type = std.substr(0, num);
-       year = lexical_cast<unsigned>(std.substr(num));
-       year += (year<70 ? 2000 : 1900);
-}
-
-string BuildInfo::LanguageStandard::str() const
-{
-       return format("%s%02d", type, year%100);
-}
-
-
-BuildInfo::Loader::Loader(BuildInfo &bi):
-       DataFile::ObjectLoader<BuildInfo>(bi)
-{
-       add("debug",    &BuildInfo::debug);
-       add("define",   &Loader::define);
-       add("incpath",  &Loader::incpath);
-       add("keep_symbol", &Loader::keep_symbol);
-       add("libpath",  &Loader::libpath);
-       add("library",  &Loader::library);
-       add("libmode",  &BuildInfo::libmode);
-       add("libmode",  &Loader::libmode_for_lib);
-       add("local_incpath", &Loader::local_incpath);
-       add("optimize", &BuildInfo::optimize);
-       add("runtime_path_mode", &BuildInfo::rpath_mode);
-       add("standard", &Loader::standard);
-       add("strip",    &BuildInfo::strip);
-       add("sysroot",  &Loader::sysroot);
-       add("threads",  &BuildInfo::threads);
-       add("warning_level", &BuildInfo::warning_level);
-       add("fatal_warnings", &BuildInfo::fatal_warnings);
-}
-
-void BuildInfo::Loader::incpath(const string &s)
-{
-       obj.incpath.push_back(s);
-}
-
-void BuildInfo::Loader::define(const string &d, const string &v)
-{
-       obj.defines[d] = v;
-}
-
-void BuildInfo::Loader::keep_symbol(const string &s)
-{
-       obj.keep_symbols.push_back(s);
-}
-
-void BuildInfo::Loader::libmode_for_lib(const string &l, LibraryMode m)
-{
-       obj.libmodes[l] = m;
-}
-
-void BuildInfo::Loader::libpath(const string &s)
-{
-       obj.libpath.push_back(s);
-}
-
-void BuildInfo::Loader::library(const string &s)
-{
-       obj.libs.push_back(s);
-}
-
-void BuildInfo::Loader::local_incpath(const string &s)
-{
-       obj.local_incpath.push_back(s);
-}
-
-void BuildInfo::Loader::standard(DataFile::Symbol tag, const string &std)
-{
-       obj.standards[tag.name] = std;
-}
-
-void BuildInfo::Loader::sysroot(const string &s)
-{
-       obj.sysroot = s;
-}
-
-
-void operator>>(const LexicalConverter &conv, BuildInfo::LibraryMode &libmode)
-{
-       if(conv.get()=="FORCE_STATIC")
-               libmode = BuildInfo::FORCE_STATIC;
-       else if(conv.get()=="STATIC")
-               libmode = BuildInfo::STATIC;
-       else if(conv.get()=="DYNAMIC")
-               libmode = BuildInfo::DYNAMIC;
-       else if(conv.get()=="FORCE_DYNAMIC")
-               libmode = BuildInfo::FORCE_DYNAMIC;
-       else
-               throw lexical_error(format("Conversion of '%s' to LibraryMode", conv.get()));
-}
-
-
-void operator>>(const LexicalConverter &conv, BuildInfo::RuntimePathMode &rpath_mode)
-{
-       if(conv.get()=="NONE")
-               rpath_mode = BuildInfo::NO_RPATH;
-       else if(conv.get()=="RELATIVE")
-               rpath_mode = BuildInfo::RELATIVE;
-       else if(conv.get()=="ABSOLUTE")
-               rpath_mode = BuildInfo::ABSOLUTE;
-       else
-               throw lexical_error(format("Conversion of '%s' to RuntimePathMode", conv.get()));
-}
diff --git a/source/buildinfo.h b/source/buildinfo.h
deleted file mode 100644 (file)
index 26afabf..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-#ifndef BUILDINFO_H_
-#define BUILDINFO_H_
-
-#include <string>
-#include <vector>
-#include <msp/datafile/objectloader.h>
-#include <msp/fs/path.h>
-
-/**
-Stores information about compiler command line parameters in a more abstract
-form.  Allows combining with other BuildInfos to support package dependencies.
-*/
-class BuildInfo
-{
-public:
-       enum LibraryMode
-       {
-               FORCE_STATIC,  //< Only accept static libraries
-               STATIC,        //< Prefer static libraries but accept dynamic as well
-               DYNAMIC,       //< Prefer dynamic libraries but accept static as well
-               FORCE_DYNAMIC  //< Only accept dynamic libraries
-       };
-
-       enum RuntimePathMode
-       {
-               NO_RPATH,  //< Do not record rpath in binaries
-               RELATIVE,  //< Record relative rpath in binaries
-               ABSOLUTE   //< Record absolute rpath in binaries
-       };
-
-       class Loader: public Msp::DataFile::ObjectLoader<BuildInfo>
-       {
-       public:
-               Loader(BuildInfo &);
-       private:
-               void incpath(const std::string &);
-               void define(const std::string &, const std::string &);
-               void keep_symbol(const std::string &);
-               void libmode_for_lib(const std::string &, LibraryMode);
-               void libpath(const std::string &);
-               void library(const std::string &);
-               void local_incpath(const std::string &);
-               void standard(Msp::DataFile::Symbol, const std::string &);
-               void sysroot(const std::string &);
-       };
-       
-       enum UpdateLevel
-       {
-               LOCAL,       //< Include all information
-               DEPENDENCY,  //< Include all but code generation options
-               CHAINED      //< Include only compilation options
-       };
-
-       struct LanguageStandard
-       {
-               std::string type;
-               unsigned year = 0;
-
-               LanguageStandard() = default;
-               LanguageStandard(const std::string &);
-
-               std::string str() const;
-       };
-
-       /**
-       A wrapper which tracks the set status of the wrapped variable.  A default
-       value may be provided in initialization without causing it to be treated as
-       set.  Assigning from a raw value flags the Tracked object as set.  Assigning
-       from another Tracked object will only change the value of the target if the
-       source is set.  
-       */
-       template<typename T>
-       class Tracked
-       {
-       public:
-               using LoadType = T;
-
-       private:
-               T value{};
-               bool set = false;
-
-       public:
-               Tracked() = default;
-               Tracked(T v): value(v) { }
-               Tracked(const Tracked &t) = default;
-               Tracked &operator=(const Tracked &v) { if(v.set) { value = v.value; set = true; } return *this; }
-
-               Tracked &operator=(const T &v) { value = v; set = true; return *this; }
-               operator const T &() const { return value; }
-       };
-
-       Tracked<Msp::FS::Path> sysroot;
-       std::map<std::string, std::string> defines;
-       std::vector<Msp::FS::Path> incpath;
-       std::vector<Msp::FS::Path> local_incpath;
-       std::vector<Msp::FS::Path> libpath;
-       std::vector<std::string> libs;
-       Tracked<LibraryMode> libmode = DYNAMIC;
-       Tracked<RuntimePathMode> rpath_mode = NO_RPATH;
-       std::map<std::string, LibraryMode> libmodes;
-       std::vector<std::string> keep_symbols;
-       std::map<std::string, LanguageStandard> standards;
-       Tracked<bool> threads = false;
-       Tracked<bool> debug = false;
-       Tracked<int> optimize = 0;
-       Tracked<bool> strip = false;
-       Tracked<unsigned> warning_level = 0;
-       Tracked<bool> fatal_warnings = false;
-
-       /** Returns the library mode for linking a particular library.  If no mode
-       has been specified for that library, the the global library mode is
-       returned. */
-       LibraryMode get_libmode_for(const std::string &) const;
-
-       /** Updates the BuildInfo from another one.  Lists are concatenated, with
-       the first occurrence of each item preserved.  Scalars are overwritten.
-       
-       The update level determines what information is updated. */
-       void update_from(const BuildInfo &, UpdateLevel = LOCAL);
-};
-
-#endif
diff --git a/source/buildtype.cpp b/source/buildtype.cpp
deleted file mode 100644 (file)
index 2485ca5..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "buildtype.h"
-
-using namespace std;
-using namespace Msp;
-
-BuildType::Loader::Loader(BuildType &b):
-       DataFile::ObjectLoader<BuildType>(b)
-{
-       add("build_info", &Loader::build_info);
-}
-
-void BuildType::Loader::build_info()
-{
-       load_sub(obj.build_info);
-}
diff --git a/source/buildtype.h b/source/buildtype.h
deleted file mode 100644 (file)
index c16e8f9..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef BUILDTYPE_H_
-#define BUILDTYPE_H_
-
-#include <string>
-#include <msp/datafile/objectloader.h>
-#include "buildinfo.h"
-
-class BuildType
-{
-public:
-       class Loader: public Msp::DataFile::ObjectLoader<BuildType>
-       {
-       public:
-               Loader(BuildType &);
-
-       private:
-               void build_info();
-       };
-
-private:
-       std::string name;
-       BuildInfo build_info;
-
-public:
-       BuildType(const std::string &n): name(n) { }
-
-       const std::string &get_name() const { return name; }
-       const BuildInfo &get_build_info() const { return build_info; }
-};
-
-#endif
diff --git a/source/builtintools.cpp b/source/builtintools.cpp
deleted file mode 100644 (file)
index c2c11e6..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "builtintools.h"
-#include "copy.h"
-#include "compilecommandsgenerator.h"
-#include "pkgconfiggenerator.h"
-#include "tar.h"
-#include "vcxprojectgenerator.h"
-#include "vssolutiongenerator.h"
-
-BuiltinTools::BuiltinTools(Builder &builder)
-{
-       add_tool(new Copy(builder));
-       add_tool(new Tar(builder));
-       add_tool(new PkgConfigGenerator(builder));
-       add_tool(new VcxProjectGenerator(builder));
-       add_tool(new VsSolutionGenerator(builder));
-       add_tool(new CompileCommandsGenerator(builder));
-}
diff --git a/source/builtintools.h b/source/builtintools.h
deleted file mode 100644 (file)
index e3b0256..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef BUILTINTOOLS_H_
-#define BUILTINTOOLS_H_
-
-#include "toolchain.h"
-
-class Builder;
-
-class BuiltinTools: public Toolchain
-{
-public:
-       BuiltinTools(Builder &);
-};
-
-#endif
diff --git a/source/cache.cpp b/source/cache.cpp
deleted file mode 100644 (file)
index 6baacce..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-#include <msp/core/maputils.h>
-#include <msp/fs/dir.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include <msp/strings/utils.h>
-#include "builder.h"
-#include "cache.h"
-#include "sourcepackage.h"
-#include "target.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace {
-
-unsigned read_count(IO::Base &in)
-{
-       unsigned char head = in.get();
-       if((head&0xC0)==0xC0)
-       {
-               unsigned char tail = in.get();
-               return (head&0x3F)<<8 | tail;
-       }
-       else
-               return head;
-}
-
-string read_string(IO::Base &in)
-{
-       unsigned len = read_count(in);
-       char buf[0x4000];
-       len = in.read(buf, len);
-       return string(buf, len);
-}
-
-void write_count(IO::Base &out, unsigned count)
-{
-       if(count<0xC0)
-               out.put(count);
-       else if(count<0x4000)
-       {
-               out.put(0xC0 | (count>>8));
-               out.put(count&0xFF);
-       }
-       else
-               throw invalid_argument("write_count");
-}
-
-void write_string(IO::Base &out, const string &str)
-{
-       write_count(out, str.size());
-       out.write(str);
-}
-
-}
-
-
-Cache::Cache(SourcePackage &p):
-       package(p),
-       filename(package.get_temp_directory()/"../cache")
-{ }
-
-void Cache::set_value(const Target *tgt, const string &k, const string &v)
-{
-       Values vl;
-       vl.push_back(v);
-       set_values(tgt, k, vl);
-}
-
-void Cache::append_value(const Target *tgt, const string &k, const string &v)
-{
-       Key key(tgt->get_name(), k);
-       auto i = data.find(key);
-       if(i==data.end())
-               i = data.insert({ key, Values() }).first;
-       i->second.push_back(v);
-       changed = true;
-       package.get_builder().get_logger().log("cache", "Updated key %s %s+ %s", tgt->get_name(), k, v);
-}
-
-void Cache::set_values(const Target *tgt, const string &k, const Values &v)
-{
-       data[Key(tgt->get_name(), k)] = v;
-       changed = true;
-       package.get_builder().get_logger().log("cache", "Updated key %s %s: %s", tgt->get_name(), k, join(v.begin(), v.end()));
-}
-
-const string &Cache::get_value(const Target *tgt, const string &k)
-{
-       const Values &values = get_values(tgt, k);
-       if(values.empty())
-               throw logic_error("values.empty()");
-       return values.front();
-}
-
-const Cache::Values &Cache::get_values(const Target *tgt, const string &k)
-{
-       return get_item(data, Key(tgt->get_name(), k));
-}
-
-bool Cache::has_key(const Target *tgt, const string &k)
-{
-       return data.count(Key(tgt->get_name(), k));
-}
-
-void Cache::load()
-{
-       if(FS::Stat st = FS::stat(filename))
-       {
-               package.get_builder().get_logger().log("files", "Reading %s", filename);
-               IO::BufferedFile in(filename.str());
-
-               while(!in.eof())
-               {
-                       Key key;
-                       key.first = read_string(in);
-                       key.second = read_string(in);
-                       if(key.first.empty() || key.second.empty())
-                               break;
-                       Values &values = data[key];
-                       for(unsigned count = read_count(in); count; --count)
-                               values.push_back(read_string(in));
-                       package.get_builder().get_logger().log("cache", "Loaded key %s %s: %s", key.first, key.second, join(values.begin(), values.end()));
-               }
-
-               mtime = st.get_modify_time();
-       }
-}
-
-void Cache::save() const
-{
-       if(data.empty() || !changed)
-               return;
-
-       FS::Path dir = FS::dirname(filename);
-       if(!FS::exists(dir))
-               FS::mkpath(dir, 0755);
-       package.get_builder().get_logger().log("files", "Writing %s", filename);
-       IO::BufferedFile out(filename.str(), IO::M_WRITE);
-
-       for(const auto &kvp: data)
-       {
-               write_string(out, kvp.first.first);
-               write_string(out, kvp.first.second);
-               write_count(out, kvp.second.size());
-               for(const string &v: kvp.second)
-                       write_string(out, v);
-       }
-
-       changed = false;
-}
diff --git a/source/cache.h b/source/cache.h
deleted file mode 100644 (file)
index 9193dac..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-#ifndef CACHE_H_
-#define CACHE_H_
-
-#include <map>
-#include <vector>
-#include <msp/fs/path.h>
-#include <msp/time/timestamp.h>
-
-class SourcePackage;
-class Target;
-
-/**
-Stores data between build runs.  This can be used to avoid scanning files again
-every time builder is run, or to detect outside changes.
-
-Data is stored as lists of strings and keyed to target and an arbitrary
-identifier.  Any kind of strings can be stored, even ones that contain
-unprintable characters or nuls.
-*/
-class Cache
-{
-public:
-       using Values = std::vector<std::string>;
-private:
-       using Key = std::pair<std::string, std::string>;
-
-       SourcePackage &package;
-       Msp::FS::Path filename;
-       std::map<Key, Values> data;
-       Msp::Time::TimeStamp mtime;
-       mutable bool changed = false;
-
-public:
-       Cache(SourcePackage &p);
-
-       /// Sets a key to a single value, replacing any existing values.
-       void set_value(const Target *, const std::string &, const std::string &);
-
-       /// Appends a value to a key.  If the key does not exist, it is created.
-       void append_value(const Target *, const std::string &, const std::string &);
-
-       /// Sets a key to a list of values, replacing any existing values.
-       void set_values(const Target *, const std::string &, const Values &);
-
-       /** Returns the first value from a key.  The key must exist and be
-       non-empty. */
-       const std::string &get_value(const Target *, const std::string &);
-
-       /// Returns the values from a key.  The key must exist.
-       const Values &get_values(const Target *, const std::string &);
-
-       /// Indicates whether a key exists.
-       bool has_key(const Target *, const std::string &);
-
-       /// Returns the last modification timestamp of the cache.
-       const Msp::Time::TimeStamp &get_mtime() const { return mtime; }
-
-       /** Loads the cache file and sets the last modification timestamp
-       accordingly. */
-       void load();
-
-       /** Saves the cache.  Does nothing if there is no data to save or nothing
-       has changed. */
-       void save() const;
-};
-
-#endif
diff --git a/source/chainedtask.cpp b/source/chainedtask.cpp
deleted file mode 100644 (file)
index a232ef8..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#include <msp/strings/utils.h>
-#include "chainedtask.h"
-
-using namespace std;
-using namespace Msp;
-
-ChainedTask::~ChainedTask()
-{
-       for(Task *t: tasks)
-               delete t;
-}
-
-void ChainedTask::add_task(Task *t)
-{
-       tasks.push_back(t);
-}
-
-string ChainedTask::get_command() const
-{
-       string cmd;
-       for(Task *t: tasks)
-               append(cmd, "\n", t->get_command());
-       return cmd;
-}
-
-void ChainedTask::start()
-{
-       prepare();
-
-       current = 0;
-       tasks[current]->start();
-}
-
-Task::Status ChainedTask::check()
-{
-       while(current<tasks.size() && !process(tasks[current]->check())) ;
-
-       return final_status;
-}
-
-Task::Status ChainedTask::wait()
-{
-       while(current<tasks.size() && !process(tasks[current]->wait())) ;
-
-       return final_status;
-}
-
-bool ChainedTask::process(Status sub_status)
-{
-       if(sub_status==SUCCESS && current+1<tasks.size())
-       {
-               // The task succeeded and there's more to run
-               ++current;
-               tasks[current]->start();
-               return true;
-       }
-
-       if(sub_status!=RUNNING)
-       {
-               // The task is not running anymore and either failed or was the last one
-               current = tasks.size();
-               final_status = sub_status;
-       }
-
-       return false;
-}
diff --git a/source/chainedtask.h b/source/chainedtask.h
deleted file mode 100644 (file)
index 30a3b3e..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef CHAINEDTASK_H_
-#define CHAINEDTASK_H_
-
-#include <vector>
-#include "task.h"
-
-/**
-Runs multiple tasks as one unit, one after the other.  Execution of the chain
-will stop if any of the component tasks terminates with an error.
-*/
-class ChainedTask: public Task
-{
-private:
-       std::vector<Task *> tasks;
-       unsigned current = 0;
-       Status final_status = RUNNING;
-
-public:
-       ChainedTask(Task *t) { add_task(t); }
-       ~ChainedTask();
-
-       void add_task(Task *);
-
-       std::string get_command() const override;
-       void start() override;
-       Status check() override;
-       Status wait() override;
-private:
-       bool process(Status);
-};
-
-#endif
diff --git a/source/clangcompiler.cpp b/source/clangcompiler.cpp
deleted file mode 100644 (file)
index bcd73d1..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#include "clangcompiler.h"
-
-using namespace std;
-using namespace Msp;
-
-ClangCompiler::ClangCompiler(Builder &b, const Architecture &a, const string &t):
-       CustomizedTool(b, t, a)
-{
-       set_command((tag=="CXX" ? "clang++" : "clang"), true);
-}
diff --git a/source/clangcompiler.h b/source/clangcompiler.h
deleted file mode 100644 (file)
index af2c05d..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef CLANGCOMPILER_H_
-#define CLANGCOMPILER_H_
-
-#include "customizedtool.h"
-
-class ClangCompiler: public CustomizedTool
-{
-public:
-       ClangCompiler(Builder &, const Architecture &, const std::string &);
-};
-
-#endif
diff --git a/source/clanglinker.cpp b/source/clanglinker.cpp
deleted file mode 100644 (file)
index 5089028..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <msp/fs/stat.h>
-#include "builder.h"
-#include "clanglinker.h"
-
-using namespace Msp;
-
-ClangLinker::ClangLinker(Builder &b, const Architecture &a):
-       CustomizedTool(b, "LINK", a)
-{
-       set_command("clang", true);
-}
-
-void ClangLinker::do_prepare(ToolData &tool) const
-{
-       parent.prepare();
-       CustomizedTool::do_prepare(tool);
-       for(const FS::Path &p: parent.get_system_path())
-               if(FS::exists(p/"libstdc++.so"))
-               {
-                       builder.get_logger().log("tools", "Got %s gcc system path: %s", static_cast<const Tool &>(tool).get_tag(), p);
-                       tool.system_path.push_back(p);
-                       break;
-               }
-}
diff --git a/source/clanglinker.h b/source/clanglinker.h
deleted file mode 100644 (file)
index de65e64..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef CLANGLINKER_H_
-#define CLANGLINKER_H_
-
-#include "customizedtool.h"
-
-class ClangLinker: public CustomizedTool
-{
-public:
-       ClangLinker(Builder &, const Architecture &);
-
-protected:
-       void do_prepare(ToolData &) const override;
-};
-
-#endif
diff --git a/source/clangtools.cpp b/source/clangtools.cpp
deleted file mode 100644 (file)
index 8e8973d..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "architecture.h"
-#include "clangcompiler.h"
-#include "clanglinker.h"
-#include "clangtools.h"
-
-using namespace std;
-
-ClangTools::ClangTools(Builder &builder, const Architecture &arch):
-       Toolchain("clang", get_priority(arch))
-{
-       add_tool(new ClangCompiler(builder, arch, "CC"));
-       add_tool(new ClangCompiler(builder, arch, "CXX"));
-       add_tool(new ClangCompiler(builder, arch, "OBJC"));
-
-       add_tool(new ClangLinker(builder, arch));
-}
-
-int ClangTools::get_priority(const Architecture &arch)
-{
-       if(arch.get_toolchain()=="clang")
-               return 20;
-       else if(arch.get_system()=="darwin" || arch.get_system()=="freebsd")
-               return 10;
-       else
-               return 0;
-}
diff --git a/source/clangtools.h b/source/clangtools.h
deleted file mode 100644 (file)
index 9faaf1a..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef CLANGTOOLS_H_
-#define CLANGTOOLS_H_
-
-#include "toolchain.h"
-
-class Architecture;
-class Builder;
-
-class ClangTools: public Toolchain
-{
-public:
-       ClangTools(Builder &, const Architecture &);
-
-       static int get_priority(const Architecture &);
-};
-
-#endif
diff --git a/source/cli/analyzer.cpp b/source/cli/analyzer.cpp
new file mode 100644 (file)
index 0000000..e12e7fc
--- /dev/null
@@ -0,0 +1,162 @@
+#include <msp/builder/builder.h>
+#include <msp/builder/buildgraph.h>
+#include <msp/builder/objectfile.h>
+#include <msp/builder/sourcefile.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/builder/target.h>
+#include <msp/builder/tool.h>
+#include <msp/core/algorithm.h>
+#include <msp/fs/utils.h>
+#include <msp/io/print.h>
+#include "analyzer.h"
+
+using namespace std;
+using namespace Msp;
+
+void Analyzer::analyze()
+{
+       if(mode==RDEPS)
+       {
+               rdepends.clear();
+               for(const auto &kvp: builder.get_build_graph().get_targets())
+               {
+                       for(Target *d: kvp.second->get_dependencies())
+                               rdepends[d].insert(kvp.second);
+                       for(Target *d: kvp.second->get_transitive_dependencies())
+                               rdepends[d].insert(kvp.second);
+               }
+       }
+
+       table.clear();
+
+       TableRow row;
+       row.push_back("Name");
+       row.push_back("Package");
+       row.push_back("Type");
+       row.push_back("Tool");
+       row.push_back("Rebuild");
+       table.push_back(row);
+       
+       Target &goals = builder.get_build_graph().get_goals();
+       if(mode==RDEPS)
+       {
+               for(Target *d: goals.get_dependencies())
+                       build_depend_table(*d, 0);
+       }
+       else
+               build_depend_table(goals, 0);
+
+       print_table();
+}
+
+void Analyzer::build_depend_table(Target &tgt, unsigned depth)
+{
+       Target *real = tgt.get_real_target();
+       if(mode==DEPS)
+       {
+               // Skip trivial targets
+               if(real!=&tgt)
+                       return build_depend_table(*real, depth);
+               if(const ObjectFile *obj = dynamic_cast<const ObjectFile *>(&tgt))
+                       return build_depend_table(obj->get_source(), depth);
+       }
+       else if(mode==REBUILD && !tgt.needs_rebuild())
+               /* All targets that depend on to-be-built targets will be rebuilt
+               themselves, so we can stop here. */
+               return;
+       
+       TableRow row;
+
+       string name;
+       const FileTarget *ft = dynamic_cast<const FileTarget *>(&tgt);
+       if(full_paths && ft)
+               name = ft->get_path().str();
+       else
+               name = tgt.get_name();
+       row.push_back(string(depth*2, ' ')+name);
+
+       const Package *pkg = tgt.get_package();
+       if(pkg)
+               row.push_back(pkg->get_name());
+       else
+               row.push_back("");
+       
+       row.push_back(tgt.get_type());
+       const Tool *tool = tgt.get_tool();
+       if(tool)
+               row.push_back(tool->get_tag());
+       else
+               row.push_back("");
+
+       if(tgt.needs_rebuild())
+               row.push_back(tgt.get_rebuild_reason());
+
+       table.push_back(row);
+
+       if(!max_depth || depth<max_depth-1)
+       {
+               Target::Dependencies depends;
+               if(mode==RDEPS)
+               {
+                       const set<Target *> &rdeps = rdepends[&tgt];
+                       depends.assign(rdeps.begin(), rdeps.end());
+               }
+               else
+               {
+                       depends = tgt.get_dependencies();
+                       const Target::Dependencies &tdeps = tgt.get_transitive_dependencies();
+                       depends.insert(depends.end(), tdeps.begin(), tdeps.end());
+               }
+
+               sort(depends, (full_paths ? target_order_full : target_order));
+
+               for(Target *d: depends)
+                       build_depend_table(*d, depth+1);
+       }
+}
+
+void Analyzer::print_table() const
+{
+       vector<string::size_type> col_width;
+
+       // Determine column widths
+       for(const vector<string> &r: table)
+       {
+               if(col_width.size()<r.size())
+                       col_width.resize(r.size(), 0);
+               for(unsigned j=0; j<r.size(); ++j)
+                       col_width[j] = max(col_width[j], r[j].size());
+       }
+
+       for(const vector<string> &r: table)
+       {
+               string line;
+               for(unsigned j=0; j<r.size(); ++j)
+               {
+                       if(j>0)
+                               line += "  ";
+                       line += lexical_cast<string>(r[j], Fmt("%-s").width(col_width[j]));
+               }
+               IO::print("%s\n", line);
+       }
+}
+
+bool Analyzer::target_order(const Target *t1, const Target *t2)
+{
+       return t1->get_name()<t2->get_name();
+}
+
+bool Analyzer::target_order_full(const Target *t1, const Target *t2)
+{
+       const FileTarget *ft1 = dynamic_cast<const FileTarget *>(t1);
+       const FileTarget *ft2 = dynamic_cast<const FileTarget *>(t2);
+       if(!ft1)
+       {
+               if(ft2)
+                       return true;
+               return target_order(t1, t2);
+       }
+       else if(!ft2)
+               return false;
+       return ft1->get_path().str()<ft2->get_path().str();
+}
diff --git a/source/cli/analyzer.h b/source/cli/analyzer.h
new file mode 100644 (file)
index 0000000..d30de04
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef ANALYZER_H_
+#define ANALYZER_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+class Builder;
+class Target;
+
+/**
+Performs various kinds of dependency analysis on the build tree.
+*/
+class Analyzer
+{
+public:
+       enum Mode
+       {
+               DEPS,     //< Skip over "trivial" targets such as InstalledFile
+               ALLDEPS,  //< Print out absolutely every target
+               REBUILD,  //< Print targets that are going to be rebuilt
+               RDEPS     //< Print targets that depend on the given targets
+       };
+
+private:
+       using TableRow = std::vector<std::string>;
+
+       Builder &builder;
+       Mode mode = DEPS;
+       std::vector<TableRow> table;
+       unsigned max_depth = 0;
+       bool full_paths = false;
+       std::map<const Target *, std::set<Target *> > rdepends;
+
+public:
+       Analyzer(Builder &b): builder(b) { }
+
+       void set_mode(Mode m) { mode = m; }
+       void set_max_depth(unsigned m) { max_depth = m; }
+       void set_full_paths(bool f) { full_paths = f; }
+
+       /// Performs the analysis and prints out the resulting dependency tree.
+       void analyze();
+
+private:
+       /** Adds rows for a target, then recursively adds rows for dependencies as
+       needed. */
+       void build_depend_table(Target &, unsigned);
+
+       /// Prints out the table that resulted from the analysis.
+       void print_table() const;
+
+       static bool target_order(const Target *, const Target *);
+       static bool target_order_full(const Target *, const Target *);
+};
+
+#endif
diff --git a/source/cli/buildercli.cpp b/source/cli/buildercli.cpp
new file mode 100644 (file)
index 0000000..cb0897c
--- /dev/null
@@ -0,0 +1,374 @@
+#include <msp/builder/filetarget.h>
+#include <msp/builder/sourcepackage.h>
+#include <msp/builder/tool.h>
+#include <msp/builder/toolchain.h>
+#include <msp/core/getopt.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#include <msp/io/print.h>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include "analyzer.h"
+#include "buildercli.h"
+
+using namespace std;
+using namespace Msp;
+
+BuilderCLI::BuilderCLI(int argc, char **argv):
+       RegisteredApplication<BuilderCLI>("builder")
+{
+       string analyze_mode;
+       string work_dir;
+       bool full_paths = false;
+       unsigned max_depth = 4;
+       string prefix;
+       string tempdir;
+       string arch;
+       bool no_externals = false;
+       unsigned verbose = 1;
+       bool silent = false;
+       vector<string> log_channels;
+       string build_type;
+
+       GetOpt getopt;
+       getopt.add_option('a', "analyze",    analyze_mode,  GetOpt::REQUIRED_ARG).set_help("Perform dependency analysis.", "MODE");
+       getopt.add_option('b', "build",      build,         GetOpt::NO_ARG).set_help("Perform build even if also doing something else.");
+       getopt.add_option('c', "clean",      clean,         GetOpt::NO_ARG).set_help("Clean buildable targets.");
+       getopt.add_option('f', "file",       build_file,    GetOpt::REQUIRED_ARG).set_help("Read build instructions from FILE.", "FILE");
+       getopt.add_option('h', "help",       help,          GetOpt::NO_ARG).set_help("Print this message.");
+       getopt.add_option('j', "jobs",       jobs,          GetOpt::REQUIRED_ARG).set_help("Run up to NUM tasks in parallel.", "NUM");
+       getopt.add_option('l', "log",        log_channels,  GetOpt::REQUIRED_ARG).set_help("Enable listed log channels.", "LIST");
+       getopt.add_option('n', "dry-run",    dry_run,       GetOpt::NO_ARG).set_help("Show what would be done without actually doing it.");
+       getopt.add_option('s', "silent",     silent,        GetOpt::NO_ARG).set_help("Don't print any messages other than errors.");
+       getopt.add_option('t', "build-type", build_type,    GetOpt::REQUIRED_ARG).set_help("Set build type.", "TYPE");
+       getopt.add_option('v', "verbose",    verbose,       GetOpt::NO_ARG).set_help("Print more information about what's going on.");
+       getopt.add_option('x', "no-externals",no_externals, GetOpt::NO_ARG).set_help("Do not load external source packages.");
+       getopt.add_option('A', "conf-all",   conf_all,      GetOpt::NO_ARG).set_help("Apply configuration to all packages.");
+       getopt.add_option('B', "build-all",  build_all,     GetOpt::NO_ARG).set_help("Build all targets unconditionally.");
+       getopt.add_option('C', "chdir",      work_dir,      GetOpt::REQUIRED_ARG).set_help("Change to DIR before doing anything else.", "DIR");
+       getopt.add_option('P', "progress",   show_progress, GetOpt::NO_ARG).set_help("Display progress while building.");
+       getopt.add_option('W', "what-if",    what_if,       GetOpt::REQUIRED_ARG).set_help("Pretend that FILE has changed.", "FILE");
+       getopt.add_option(     "arch",       arch,          GetOpt::REQUIRED_ARG).set_help("Build for architecture ARCH.", "ARCH");
+       getopt.add_option(     "conf-only",  conf_only,     GetOpt::NO_ARG).set_help("Stop after configuring packages.");
+       getopt.add_option(     "full-paths", full_paths,    GetOpt::NO_ARG).set_help("Output full paths in analysis.");
+       getopt.add_option(     "max-depth",  max_depth,     GetOpt::REQUIRED_ARG).set_help("Show up to NUM levels in analysis.", "NUM");
+       getopt.add_option(     "prefix",     prefix,        GetOpt::REQUIRED_ARG).set_help("Install things to DIR.", "DIR");
+       getopt.add_option(     "tempdir",    tempdir,       GetOpt::REQUIRED_ARG).set_help("Store temporary files in DIR.", "DIR");
+       getopt.add_argument("target", cmdline_targets, GetOpt::OPTIONAL_ARG).set_help("Target(s) to build");
+       getopt(argc, argv);
+
+       if(help)
+       {
+               helpmsg = "Usage:\n  ";
+               helpmsg += getopt.generate_usage(argv[0], true);
+               helpmsg += "\n\n";
+               helpmsg += getopt.generate_help();
+       }
+
+       if(silent)
+               --verbose;
+       if(verbose>=1)
+       {
+               logger.enable_channel("summary");
+               logger.enable_channel("tasks");
+       }
+       if(verbose>=2)
+       {
+               logger.enable_channel("environment");
+               logger.enable_channel("packages");
+               logger.enable_channel("commands");
+       }
+       if(verbose>=3)
+       {
+               logger.enable_channel("files");
+               logger.enable_channel("auxcommands");
+       }
+       for(const string &c: log_channels)
+               for(const string &p: split(c, ','))
+                       logger.enable_channel(p);
+       builder.set_logger(&logger);
+
+       if(!analyze_mode.empty())
+       {
+               analyzer = new Analyzer(builder);
+
+               if(analyze_mode=="deps")
+                       analyzer->set_mode(Analyzer::DEPS);
+               else if(analyze_mode=="alldeps")
+                       analyzer->set_mode(Analyzer::ALLDEPS);
+               else if(analyze_mode=="rebuild")
+                       analyzer->set_mode(Analyzer::REBUILD);
+               else if(analyze_mode=="rdeps")
+                       analyzer->set_mode(Analyzer::RDEPS);
+               else
+                       throw usage_error("Invalid analyze mode");
+
+               analyzer->set_max_depth(max_depth);
+               analyzer->set_full_paths(full_paths);
+       }
+       else if(!clean && !create_makefile)
+               build = true;
+
+       if(!work_dir.empty())
+               FS::chdir(work_dir);
+
+       cwd = FS::getcwd();
+
+       PackageManager &package_manager = builder.get_package_manager();
+
+       package_manager.append_package_path(cwd);
+       package_manager.append_package_path(cwd/"..");
+       package_manager.append_binary_package_path(FS::get_sys_data_dir()/"packages");
+
+       package_manager.set_no_externals(no_externals);
+
+       builder.set_architecture(tolower(arch));
+
+       vector<FS::Path> start_files;
+       start_files.push_back(FS::get_sys_data_dir()/"builderrc");
+       start_files.push_back(FS::get_user_data_dir()/"rc");
+       for(const FS::Path &f: start_files)
+               if(FS::exists(f))
+                       builder.load_build_file(f);
+
+       if(!prefix.empty())
+               builder.set_prefix(cwd/prefix);
+
+       if(!tempdir.empty())
+               builder.set_temp_directory(tempdir);
+
+       if(!build_type.empty())
+               builder.set_build_type(build_type);
+
+       builder.add_default_tools();
+
+       const Toolchain &toolchain = builder.get_toolchain();
+       for(auto i=cmdline_targets.begin(); i!=cmdline_targets.end(); )
+       {
+               string::size_type equal = i->find('=');
+               if(equal!=string::npos)
+               {
+                       string key = i->substr(0, equal);
+                       string value = i->substr(equal+1);
+                       if(toolchain.has_tool(key))
+                               toolchain.get_tool(key).set_command(value);
+                       else
+                               cmdline_options.insert({ key, value });
+                       i = cmdline_targets.erase(i);
+               }
+               else
+                       ++i;
+       }
+}
+
+BuilderCLI::~BuilderCLI()
+{
+       delete analyzer;
+}
+
+int BuilderCLI::main()
+{
+       FS::Path main_file = cwd/build_file;
+       if(FS::exists(main_file))
+       {
+               builder.load_build_file(main_file, &cmdline_options, conf_all);
+               if(!dry_run && !cmdline_options.empty())
+                       builder.save_caches();
+       }
+       else if(!help)
+       {
+               IO::print(IO::cerr, "The file %s does not exist.\n", main_file);
+               return 1;
+       }
+
+       if(help)
+       {
+               IO::print("Builder 3.0\n"
+                       "Copyright Â© 2006-2022  Mikkosoft Productions, Mikko Rasa\n"
+                       "Licensed under the GPL\n\n"
+                       "%s", helpmsg);
+               package_help();
+               return 0;
+       }
+
+       const Architecture &native_arch = builder.get_native_arch();
+       const Architecture &current_arch = builder.get_current_arch();
+       logger.log("environment", "Building on %s, for %s%s", native_arch.get_name(),
+               current_arch.get_name(), (current_arch.is_native() ? " (native)" : ""));
+       logger.log("environment", "Prefix is %s", builder.get_prefix());
+       const FS::Path &tempdir = builder.get_temp_directory();
+       if(tempdir.is_absolute())
+               logger.log("environment", "Temporary directory is %s", tempdir);
+       else
+               logger.log("environment", "Using per-package temporary directory %s", tempdir);
+       const BuildType &build_type = builder.get_build_type();
+       logger.log("environment", "Build type is %s", build_type.get_name());
+
+       if(!prepare_build())
+               return 1;
+
+       if(conf_only)
+               return 0;
+
+       BuildGraph &build_graph = builder.get_build_graph();
+       PackageManager &package_manager = builder.get_package_manager();
+       vector<string> package_details;
+       for(const auto &kvp: package_manager.get_packages())
+       {
+               if(!kvp.second->is_prepared())
+                       continue;
+
+               string line = kvp.second->get_name();
+               if(dynamic_cast<SourcePackage *>(kvp.second))
+               {
+                       line += '*';
+
+                       unsigned count = 0;
+                       unsigned to_be_built = 0;
+                       for(const auto &kvp2: build_graph.get_targets())
+                               if(kvp2.second->get_package()==kvp.second)
+                               {
+                                       ++count;
+                                       if(kvp2.second->needs_rebuild())
+                                               ++to_be_built;
+                               }
+                       if(count)
+                       {
+                               line += format(" (%d targets", count);
+                               if(to_be_built)
+                                       line += format(", %d to be built", to_be_built);
+                               line += ')';
+                       }
+               }
+
+               package_details.push_back(line);
+       }
+
+       logger.log("summary", "%d active packages, %d targets", package_details.size(), build_graph.get_targets().size());
+       for(const string &d: package_details)
+               logger.log("packages", d);
+
+       if(analyzer)
+               analyzer->analyze();
+
+       if(build_graph.get_goals().is_broken())
+       {
+               vector<string> problems = builder.collect_problems();
+               IO::print(IO::cerr, "The following problems were detected:\n");
+               for(const string &p: problems)
+                       IO::print(IO::cerr, "  %s\n", p);
+               if(!analyzer)
+                       IO::print(IO::cerr, "Please fix them and try again.\n");
+               return 1;
+       }
+
+       if(clean)
+               exit_code = builder.clean(clean>=2, dry_run);
+       if(build)
+               exit_code = builder.build(jobs, dry_run, show_progress);
+
+       if(!dry_run)
+               builder.save_caches();
+
+       return exit_code;
+}
+
+bool BuilderCLI::prepare_build()
+{
+       /* XXX This is ugly; using the Builder class should not be this convoluted.
+       But the command line targets and what ifs need to be set at certain points
+       during preparation. */
+       BuildGraph &build_graph = builder.get_build_graph();
+       PackageManager &package_manager = builder.get_package_manager();
+
+       package_manager.get_main_package().prepare();
+
+       // Add targets from command line as goals
+       for(const string &t: cmdline_targets)
+       {
+               Target *tgt = resolve_target(t);
+               if(!tgt)
+               {
+                       IO::print("I don't know anything about %s\n", t);
+                       return false;
+               }
+
+               build_graph.add_goal(*tgt);
+       }
+
+       build_graph.prepare();
+
+       // Apply what-ifs
+       for(const string &w: what_if)
+       {
+               FileTarget *tgt = dynamic_cast<FileTarget *>(resolve_target(w));
+               if(!tgt)
+               {
+                       IO::print(IO::cerr, "Unknown what-if target %s\n", w);
+                       return false;
+               }
+               tgt->touch();
+       }
+
+       if(build_all)
+               build_graph.force_full_rebuild();
+
+       if(!dry_run)
+               package_manager.save_all_caches();
+
+       return true;
+}
+
+Target *BuilderCLI::resolve_target(const string &name)
+{
+       Target *tgt = builder.get_build_graph().get_target(name);
+       if(!tgt)
+               tgt = builder.get_vfs().get_target(cwd/name);
+       return tgt;
+}
+
+void BuilderCLI::package_help()
+{
+       PackageManager &package_manager = builder.get_package_manager();
+       if(package_manager.get_packages().empty())
+               return;
+
+       SourcePackage &main_pkg = dynamic_cast<SourcePackage &>(package_manager.get_main_package());
+       const Config &config = main_pkg.get_config();
+       const auto &options = config.get_options();
+       const Package::Requirements &requires = main_pkg.get_required_packages();
+
+       if(!requires.empty() || !options.empty())
+               IO::print("\nHelp for package %s:\n", main_pkg.get_name());
+
+       if(!requires.empty())
+       {
+               IO::print("\nRequired packages:\n  ");
+               for(auto i=requires.begin(); i!=requires.end(); ++i)
+               {
+                       if(i!=requires.begin())
+                               IO::print(", ");
+                       IO::print((*i)->get_name());
+               }
+               IO::print("\n");
+       }
+
+       if(!options.empty())
+       {
+               IO::print("\nPackage configuration:\n");
+               for(const auto &kvp: options)
+               {
+                       const Config::Option &opt = kvp.second;
+                       string line = format("  %s: %s (%s)", opt.name, opt.description, opt.value);
+                       if(!opt.choices.empty())
+                       {
+                               line += " {";
+                               line += join(opt.choices.begin(), opt.choices.end(), " ");
+                               line += '}';
+                       }
+                       else if(opt.value!=opt.default_value)
+                               line += format(" [%s]", opt.default_value);
+                       IO::print("%s\n", line);
+               }
+       }
+}
diff --git a/source/cli/buildercli.h b/source/cli/buildercli.h
new file mode 100644 (file)
index 0000000..f4b2ad7
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef BUILDERCLI_H_
+#define BUILDERCLI_H_
+
+#include <msp/builder/builder.h>
+#include <msp/core/application.h>
+
+class Analyzer;
+
+/**
+Provides a command-line interface for Builder.
+*/
+class BuilderCLI: public Msp::RegisteredApplication<BuilderCLI>
+{
+private:
+       std::vector<std::string> cmdline_targets;
+       Config::InputOptions cmdline_options;
+       Msp::FS::Path cwd;
+
+       Builder builder;
+       Logger logger;
+       Analyzer *analyzer = 0;
+       bool build = false;
+       unsigned clean = 0;
+       bool dry_run = false;
+       bool help = false;
+       std::string helpmsg;
+       bool show_progress = false;
+       std::string build_file = "Build";
+       unsigned jobs = 1;
+       std::vector<std::string> what_if;
+       bool conf_all = false;
+       bool conf_only = false;
+       bool build_all = false;
+       bool create_makefile = false;
+
+public:
+       BuilderCLI(int, char **);
+       ~BuilderCLI();
+
+       int main() override;
+
+private:
+       bool prepare_build();
+       Target *resolve_target(const std::string &);
+
+       void package_help();
+};
+
+#endif
diff --git a/source/compilecommandsgenerator.cpp b/source/compilecommandsgenerator.cpp
deleted file mode 100644 (file)
index bbd4e98..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include <msp/strings/utils.h>
-#include "builder.h"
-#include "compilecommandsgenerator.h"
-#include "compilecommandsjson.h"
-#include "internaltask.h"
-#include "objectfile.h"
-#include "sourcefile.h"
-
-using namespace std;
-using namespace Msp;
-
-CompileCommandsGenerator::CompileCommandsGenerator(Builder &b):
-       Tool(b, "CCJG")
-{
-       set_run_internal(_run);
-}
-
-Target *CompileCommandsGenerator::create_target(const vector<Target *> &, const string &)
-{
-       throw logic_error("Not implemented");
-}
-
-bool CompileCommandsGenerator::_run(const CompileCommandsJson &cmds)
-{
-       Builder &builder = cmds.get_package()->get_builder();
-       const SourcePackage &spkg = *cmds.get_package();
-       string work_dir = c_escape(spkg.get_source_directory().str());
-
-       IO::BufferedFile out(cmds.get_path().str(), IO::M_WRITE);
-       IO::print(out, "[");
-
-       bool first = true;
-       for(const auto &kvp: builder.get_build_graph().get_targets())
-               if(kvp.second->is_buildable() && kvp.second->get_package()==&spkg)
-                       if(ObjectFile *obj = dynamic_cast<ObjectFile *>(kvp.second))
-                       {
-                               FS::Path src_path = obj->get_source().get_path();
-                               Task *task = kvp.second->build();
-                               if(!first)
-                                       out.put(',');
-                               IO::print(out, "\n\t{\n");
-                               IO::print(out, "\t\t\"file\": \"%s\"\n", src_path);
-                               IO::print(out, "\t\t\"command\": \"%s\"\n", c_escape(task->get_command()));
-                               IO::print(out, "\t\t\"directory\": \"%s\"\n", work_dir);
-                               IO::print(out, "\t}");
-                               delete task;
-                               first = false;
-                       }
-
-       IO::print(out, "\n]\n");
-
-       return true;
-}
diff --git a/source/compilecommandsgenerator.h b/source/compilecommandsgenerator.h
deleted file mode 100644 (file)
index a6fa6f9..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef COMPILECOMMANDSGENERATOR_H_
-#define COMPILECOMMANDSGENERATOR_H_
-
-#include "tool.h"
-
-class CompileCommandsJson;
-
-class CompileCommandsGenerator: public Tool
-{
-public:
-       CompileCommandsGenerator(Builder &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static bool _run(const CompileCommandsJson &);
-};
-
-#endif
diff --git a/source/compilecommandsjson.cpp b/source/compilecommandsjson.cpp
deleted file mode 100644 (file)
index ff24d7e..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "builder.h"
-#include "package.h"
-#include "compilecommandsjson.h"
-#include "objectfile.h"
-
-CompileCommandsJson::CompileCommandsJson(Builder &b, const SourcePackage &p):
-       FileTarget(b, p, p.get_source_directory()/("compile_commands.json"))
-{
-       tool = &builder.get_toolchain().get_tool("CCJG");
-}
-
-void CompileCommandsJson::find_dependencies()
-{
-       for(const auto &kvp: builder.get_build_graph().get_targets())
-               if(kvp.second->is_buildable() && kvp.second->get_package()==package && dynamic_cast<ObjectFile *>(kvp.second))
-                       kvp.second->prepare();
-}
diff --git a/source/compilecommandsjson.h b/source/compilecommandsjson.h
deleted file mode 100644 (file)
index 8798849..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef COMPILECOMMANDSJSON_H_
-#define COMPILECOMMANDSJSON_H_
-
-#include "sourcepackage.h"
-#include "filetarget.h"
-
-class CompileCommandsJson: public FileTarget
-{
-private:
-       
-public:
-       CompileCommandsJson(Builder &, const SourcePackage &);
-
-       const char *get_type() const override { return "CompileCommandsJson"; }
-
-protected:
-       void find_dependencies() override;
-};
-
-#endif
diff --git a/source/component.cpp b/source/component.cpp
deleted file mode 100644 (file)
index 4fddcef..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-#include <deque>
-#include <msp/core/algorithm.h>
-#include <msp/fs/dir.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include "builder.h"
-#include "component.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-void Component::prepare()
-{
-       for(Package *r: requires)
-               r->prepare();
-}
-
-void Component::create_build_info()
-{
-       BuildInfo final_build_info;
-
-       const Package::Requirements &pkg_reqs = package.get_required_packages();
-       Package::Requirements direct_reqs = requires;
-       direct_reqs.insert(direct_reqs.end(), pkg_reqs.begin(), pkg_reqs.end());
-       for(Package *r: direct_reqs)
-               final_build_info.update_from(r->get_exported_build_info(), BuildInfo::DEPENDENCY);
-
-       Package::Requirements all_reqs = direct_reqs;
-       deque<Package *> queue(direct_reqs.begin(), direct_reqs.end());
-       while(!queue.empty())
-       {
-               Package *req = queue.front();
-               queue.pop_front();
-
-               for(Package *r: req->get_required_packages())
-                       if(!any_equals(all_reqs, r))
-                       {
-                               final_build_info.update_from(r->get_exported_build_info(), BuildInfo::CHAINED);
-                               all_reqs.push_back(r);
-                               queue.push_back(r);
-                       }
-       }
-
-       final_build_info.update_from(package.get_build_info());
-       final_build_info.update_from(build_info);
-       build_info = final_build_info;
-
-       for(FS::Path &p: build_info.incpath)
-               p = (package.get_source_directory()/p).str();
-       for(FS::Path &p: build_info.libpath)
-               p = (package.get_source_directory()/p).str();
-}
-
-BuildInfo Component::get_build_info_for_path(const FS::Path &path) const
-{
-       // XXX Cache these and check that the directories actually exist before adding them
-       BuildInfo binfo = build_info;
-
-       FS::Path gen_dir = package.get_temp_directory()/"generated";
-       if(FS::descendant_depth(path, gen_dir)>=0)
-       {
-               FS::Path subdir = FS::dirname(FS::relative(path, gen_dir));
-               binfo.local_incpath.push_back(package.get_source_directory()/subdir);
-       }
-       else
-       {
-               FS::Path subdir = FS::dirname(FS::relative(path, package.get_source_directory()));
-               binfo.local_incpath.push_back(gen_dir/subdir);
-       }
-
-       if(!overlays.empty())
-       {
-               FS::Path dir = FS::dirname(path);
-               string last = FS::basename(dir);
-               if(any_equals(overlays, last))
-                       dir = FS::dirname(dir);
-
-               if(any_equals(sources, dir))
-               {
-                       binfo.local_incpath.push_back(dir);
-                       for(const string &o: overlays)
-                               binfo.local_incpath.push_back(dir/o);
-               }
-       }
-       return binfo;
-}
-
-vector<FS::Path> Component::collect_source_files() const
-{
-       vector<FS::Path> files;
-       for(const FS::Path &p: sources)
-       {
-               if(FS::is_dir(p))
-               {
-                       vector<FS::Path> dirs;
-                       dirs.reserve(1+overlays.size());
-                       dirs.push_back(p);
-                       for(const string &o: overlays)
-                       {
-                               FS::Path opath = p/o;
-                               if(FS::is_dir(opath))
-                                       dirs.push_back(opath);
-                       }
-                       set<string> overlay_files;
-                       for(auto j=dirs.begin(); j!=dirs.end(); ++j)
-                       {
-                               package.get_builder().get_logger().log("files", "Traversing %s", *j);
-                               for(const string &f: list_files(*j))
-                               {
-                                       if(j!=dirs.begin())
-                                       {
-                                               if(overlay_files.count(f))
-                                                       continue;
-                                               overlay_files.insert(f);
-                                       }
-                                       FS::Path fn = *j/f;
-                                       if(!FS::is_dir(fn))
-                                               files.push_back(fn);
-                               }
-                       }
-               }
-               else
-               {
-                       files.push_back(p);
-                       for(const string &o: overlays)
-                       {
-                               FS::Path opath = FS::dirname(p)/o/FS::basename(p);
-                               if(FS::is_reg(opath))
-                                       files.push_back(opath);
-                       }
-               }
-       }
-
-       return files;
-}
-
-
-Component::Loader::Loader(Component &c):
-       DataFile::ObjectLoader<Component>(c),
-       ConditionalLoader(c.package, format("%s/%s", c.package.get_name(), c.name))
-{
-       add("overlay",         &Loader::overlay);
-       add("source",          &Loader::source);
-       add("install",         &Component::install);
-       add("install_map",     &Loader::install_map);
-       add("build_info",      &Loader::build_info);
-       add("require",         &Loader::require);
-       add("default",         &Component::deflt);
-}
-
-void Component::Loader::build_info()
-{
-       load_sub(obj.build_info);
-}
-
-void Component::Loader::install_map()
-{
-       load_sub(obj.install_map, obj.package.get_source_directory());
-}
-
-void Component::Loader::overlay(const string &o)
-{
-       obj.overlays.push_back(o);
-}
-
-void Component::Loader::require(const string &n)
-{
-       Package *req = obj.package.get_builder().get_package_manager().find_package(n);
-       if(req)
-               obj.requires.push_back(req);
-       else
-               obj.problems.push_back(format("Required package %s not found", n));
-}
-
-void Component::Loader::source(const string &s)
-{
-       obj.sources.push_back((obj.package.get_source_directory()/s).str());
-}
diff --git a/source/component.h b/source/component.h
deleted file mode 100644 (file)
index 7ddd972..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef COMPONENT_H_
-#define COMPONENT_H_
-
-#include <string>
-#include <msp/datafile/objectloader.h>
-#include <msp/fs/path.h>
-#include "buildinfo.h"
-#include "conditionalloader.h"
-#include "installmap.h"
-#include "package.h"
-
-class SourcePackage;
-
-/**
-Components specify things to be built.  Each component may build one binary (it
-may also build none), as well as install a bunch of headers.  Components inherit
-dependencies and build info from the package they belong to, and may also add
-their own.
-*/
-class Component
-{
-public:
-       class Loader: public Msp::DataFile::ObjectLoader<Component>, public ConditionalLoader
-       {
-       public:
-               Loader(Component &);
-       private:
-               void build_info();
-               void install_map();
-               void overlay(const std::string &);
-               void require(const std::string &);
-               void source(const std::string &);
-       };
-
-protected:
-       SourcePackage &package;
-       std::string name;
-       std::vector<Msp::FS::Path> sources;
-       std::vector<std::string> overlays;
-       bool install = false;
-       BuildInfo build_info;
-       Package::Requirements requires;
-       bool deflt = true;
-       InstallMap install_map;
-       std::vector<std::string> problems;
-
-       Component(SourcePackage &p, const std::string &n): package(p), name(n) { }
-public:
-       virtual ~Component() { }
-
-       const SourcePackage &get_package() const { return package; }
-       const std::string &get_name() const { return name; }
-
-       /** Returns a list of sources for the component.  They may refer to
-       directories or individual files. */
-       const std::vector<Msp::FS::Path> &get_sources() const { return sources; }
-
-       const std::vector<std::string> &get_overlays() const { return overlays; }
-
-protected:
-       /** Returns a list of all source files for the component. */
-       std::vector<Msp::FS::Path> collect_source_files() const;
-
-public:
-       bool get_install() const { return install; }
-       const InstallMap &get_install_map() const { return install_map; }
-       const Package::Requirements &get_required_packages() const { return requires; }
-       bool is_default() const { return deflt; }
-       const std::vector<std::string> &get_problems() const { return problems; }
-
-       /** Prepares any required packages. */
-       void prepare();
-
-       /** Prepares the build information for building.  Pulls build info from the
-       parent and dependency packages, and adds any component-specific flags. */
-       virtual void create_build_info();
-
-       virtual void update_exported_build_info(BuildInfo &) const { }
-
-       const BuildInfo &get_build_info() const { return build_info; }
-
-       BuildInfo get_build_info_for_path(const Msp::FS::Path &) const;
-
-       virtual void create_targets() const = 0;
-};
-
-#endif
diff --git a/source/conditionalloader.cpp b/source/conditionalloader.cpp
deleted file mode 100644 (file)
index b9d9d25..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#include "booleanevaluator.h"
-#include "builder.h"
-#include "conditionalloader.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-ArchitectureConditional::ArchitectureConditional(const Builder &b, const string &l):
-       builder(b),
-       log_prefix(l)
-{
-       add("if_arch", &ArchitectureConditional::if_arch);
-}
-
-void ArchitectureConditional::if_arch(const string &cond)
-{
-       const Architecture &arch = builder.get_current_arch();
-       BooleanEvaluator eval([&arch](const string &value){ return arch.match_name(value); });
-       bool match = eval.evaluate(cond);
-       builder.get_logger().log("configure", "%s: arch %s %smatched", log_prefix, cond, (match ? "" : "not "));
-       if(match)
-               load_sub_with(*this);
-}
-
-
-FeatureConditional::FeatureConditional(const SourcePackage &p, const string &l):
-       package(p),
-       log_prefix(l)
-{
-       add("if_feature", &FeatureConditional::if_feature);
-}
-
-void FeatureConditional::if_feature(const string &cond)
-{
-       BooleanEvaluator eval([this](const string &feat, const string *value){ return package.match_feature(feat, value); });
-       bool match = eval.evaluate(cond);
-       package.get_builder().get_logger().log("configure", "%s: feature %s %smatched", log_prefix, cond, (match ? "" : "not "));
-       if(match)
-               load_sub_with(*this);
-}
-
-
-ConditionalLoader::ConditionalLoader(const SourcePackage &p, const string &l):
-       ArchitectureConditional(p.get_builder(), l),
-       FeatureConditional(p, l)
-{ }
diff --git a/source/conditionalloader.h b/source/conditionalloader.h
deleted file mode 100644 (file)
index 5184fac..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef CONDITIONALLOADER_H_
-#define CONDITIONALLOADER_H_
-
-#include <string>
-#include <msp/datafile/loader.h>
-
-class Builder;
-class SourcePackage;
-
-class ArchitectureConditional: virtual public Msp::DataFile::Loader
-{
-private:
-       const Builder &builder;
-       std::string log_prefix;
-
-protected:
-       ArchitectureConditional(const Builder &, const std::string &);
-
-private:
-       void if_arch(const std::string &);
-};
-
-
-class FeatureConditional: virtual public Msp::DataFile::Loader
-{
-private:
-       const SourcePackage &package;
-       std::string log_prefix;
-
-protected:
-       FeatureConditional(const SourcePackage &, const std::string &);
-
-       void if_feature(const std::string &);
-};
-
-
-class ConditionalLoader: public ArchitectureConditional, FeatureConditional
-{
-protected:
-       ConditionalLoader(const SourcePackage &, const std::string &);
-};
-
-#endif
diff --git a/source/config.cpp b/source/config.cpp
deleted file mode 100644 (file)
index 1d3b180..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-#include <msp/core/maputils.h>
-#include <msp/datafile/writer.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include <msp/time/utils.h>
-#include "builder.h"
-#include "config.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-const Config::Option &Config::add_option(const Feature &f)
-{
-       Option opt(f);
-       auto i = pending_options.find(opt.name);
-       if(i!=pending_options.end())
-               opt.value = i->second;
-
-       return options.insert({ opt.name, opt }).first->second;
-}
-
-bool Config::set_option(const string &opt, const string &val)
-{
-       bool result = false;
-
-       auto i = options.find(opt);
-       if(i!=options.end())
-       {
-               if(i->second.value!=val)
-               {
-                       result = true;
-                       changed = true;
-                       mtime = Time::now();
-               }
-               i->second.value = val;
-       }
-
-       return result;
-}
-
-bool Config::is_option(const string &name) const
-{
-       return options.count(name);
-}
-
-const Config::Option &Config::get_option(const string &name) const
-{
-       return get_item(options, name);
-}
-
-void Config::load()
-{
-       FS::Path fn = package.get_source_directory()/".config";
-       FS::Stat stat = FS::stat(fn);
-       if(stat)
-       {
-               package.get_builder().get_logger().log("files", "Reading %s", fn);
-               IO::BufferedFile in(fn.str());
-
-               mtime = stat.get_modify_time();
-
-               DataFile::Parser parser(in, fn.str());
-               Loader loader(*this);
-               loader.load(parser);
-       }
-}
-
-void Config::save() const
-{
-       if(!changed)
-               return;
-
-       FS::Path fn = package.get_source_directory()/".config";
-
-       package.get_builder().get_logger().log("files", "Writing %s", fn);
-       IO::BufferedFile out(fn.str(), IO::M_WRITE);
-       DataFile::Writer writer(out);
-
-       for(const auto &kvp: options)
-               writer.write((DataFile::Statement("option"), kvp.second.name, kvp.second.value));
-
-       changed = false;
-}
-
-
-Config::Option::Option(const Feature &f):
-       Feature(f),
-       value(default_value)
-{
-       name = "with_"+name;
-}
-
-
-Config::Loader::Loader(Config &c):
-       DataFile::ObjectLoader<Config>(c)
-{
-       add("option", &Loader::option);
-}
-
-void Config::Loader::option(const string &n, const string &v)
-{
-       if(obj.options.count(n))
-               obj.set_option(n, v);
-       else
-               obj.pending_options[n] = v;
-}
diff --git a/source/config.h b/source/config.h
deleted file mode 100644 (file)
index ba8416b..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef CONFIG_H_
-#define CONFIG_H_
-
-#include <map>
-#include <string>
-#include <msp/datafile/loader.h>
-#include <msp/fs/path.h>
-#include <msp/time/timestamp.h>
-#include "feature.h"
-
-class SourcePackage;
-
-/**
-Manages configuration for a package.  A configuration may have an arbitary
-amount of options, as well as a modification time (mtime).
-*/
-class Config
-{
-public:
-       /** A single configuration option. */
-       struct Option: public Feature
-       {
-               std::string value;
-
-               Option(const Feature &);
-       };
-
-       using InputOptions = std::map<std::string, std::string>;
-
-private:
-       class Loader: public Msp::DataFile::ObjectLoader<Config>
-       {
-       public:
-               Loader(Config &);
-       private:
-               void option(const std::string &, const std::string &);
-       };
-
-       SourcePackage &package;
-       std::map<std::string, Option> options;
-       InputOptions pending_options;
-       Msp::Time::TimeStamp mtime;
-       mutable bool changed = false;
-
-public:
-       Config(SourcePackage &p): package(p) { }
-
-       /** Adds a configuration option based on a feature. */
-       const Option &add_option(const Feature &);
-
-       bool set_option(const std::string &, const std::string &);
-
-       /** Checks whether an option exists. */
-       bool is_option(const std::string &) const;
-
-       /** Gets a configuration option by name. */
-       const Option &get_option(const std::string &) const;
-
-       const std::map<std::string, Option> &get_options() const { return options; }
-       const Msp::Time::TimeStamp &get_mtime() const { return mtime; }
-
-       void load();
-       void save() const;
-};
-
-#endif
diff --git a/source/copy.cpp b/source/copy.cpp
deleted file mode 100644 (file)
index 8f6b81d..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef _WIN32
-#include <unistd.h>
-#include <sys/stat.h>
-#endif
-#include <msp/fs/dir.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include "builder.h"
-#include "copy.h"
-#include "installedfile.h"
-#include "internaltask.h"
-
-using namespace std;
-using namespace Msp;
-
-Copy::Copy(Builder &b):
-       Tool(b, "CP")
-{
-       set_run_internal(_run);
-}
-
-Target *Copy::create_target(const vector<Target *> &sources, const string &arg)
-{
-       FileTarget &file_tgt = dynamic_cast<FileTarget &>(*sources.front());
-       InstalledFile *inst = new InstalledFile(builder, *file_tgt.get_package(), file_tgt, arg);
-       inst->set_tool(*this);
-       return inst;
-}
-
-bool Copy::_run(const InstalledFile &install)
-{
-       const FileTarget &source = install.get_source();
-       const FS::Path &src_path = source.get_path();
-       const FS::Path &dst_path = install.get_path();
-
-       try
-       {
-               IO::File in(src_path.str());
-               IO::File out(dst_path.str(), IO::M_WRITE);
-
-               // Actual transfer loop
-               char buf[16384];
-               while(!in.eof())
-               {
-                       unsigned len = in.read(buf, sizeof(buf));
-                       out.write(buf, len);
-               }
-       }
-       catch(const exception &e)
-       {
-               IO::print(IO::cerr, "%s\n", e.what());
-               return false;
-       }
-
-#ifndef _WIN32
-       // Preserve file permissions
-       struct stat st;
-       if(stat(src_path.str().c_str(), &st)==0)
-               chmod(dst_path.str().c_str(), st.st_mode&0777);
-
-       const FS::Path &link = install.get_symlink();
-       if(!link.empty())
-       {
-               FS::Path relpath = FS::relative(dst_path, FS::dirname(link));
-               if(FS::exists(link))
-                       FS::unlink(link);
-               symlink(relpath.str().c_str(), link.str().c_str());
-       }
-#endif
-
-       return true;
-}
diff --git a/source/copy.h b/source/copy.h
deleted file mode 100644 (file)
index d65bb05..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef COPY_H_
-#define COPY_H_
-
-#include "tool.h"
-
-class InstalledFile;
-
-/**
-Copies a file to another place.  Used by the InstalledFile target.
-*/
-class Copy: public Tool
-{
-public:
-       Copy(Builder &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static bool _run(const InstalledFile &);
-};
-
-#endif
diff --git a/source/csourcefile.cpp b/source/csourcefile.cpp
deleted file mode 100644 (file)
index bc76605..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#include <msp/core/maputils.h>
-#include <msp/fs/utils.h>
-#include <msp/io/print.h>
-#include <msp/strings/regex.h>
-#include "builder.h"
-#include "component.h"
-#include "csourcefile.h"
-#include "sourcepackage.h"
-#include "tool.h"
-
-using namespace std;
-using namespace Msp;
-
-CSourceFile::CSourceFile(Builder &b, const Component &c, const FS::Path &p):
-       SourceFile(b, c, p)
-{
-       string ext = FS::extpart(FS::basename(path));
-       if(ext==".h" || ext==".H" || ext==".hpp")
-               install_location = FS::Path("include")/package->get_name();
-}
-
-void CSourceFile::parse_includes(IO::Base &in)
-{
-       static Regex r_include("^[ \t]*#include[ \t]+([\"<].*)[\">]");
-
-       string line;
-       while(in.getline(line))
-               if(RegMatch match = r_include.match(line))
-                       includes.push_back(match[1].str);
-}
-
-void CSourceFile::find_dependencies()
-{
-       if(!component || !mtime)
-               return;
-
-       const SourcePackage &spkg = component->get_package();
-
-       Cache &cache = spkg.get_cache();
-       if(mtime<cache.get_mtime() && cache.has_key(this, "includes"))
-               includes = cache.get_values(this, "includes");
-       else
-       {
-               IO::BufferedFile in(path.str());
-
-               builder.get_logger().log("files", "Reading includes from %s", path.str());
-
-               parse_includes(in);
-               cache.set_values(this, "includes", includes);
-       }
-
-       const BuildInfo &build_info = component->get_build_info_for_path(path);
-       const auto &incpath = build_info.incpath;
-       VirtualFileSystem::SearchPath local_incpath;
-       local_incpath.reserve(1+build_info.local_incpath.size()+incpath.size());
-       local_incpath.push_back(FS::dirname(path).str());
-       local_incpath.insert(local_incpath.end(), build_info.local_incpath.begin(), build_info.local_incpath.end());
-       local_incpath.insert(local_incpath.end(), incpath.begin(), incpath.end());
-
-       Tool *compiler = builder.get_toolchain().get_tool_for_suffix(FS::extpart(FS::basename(path)), true);
-       if(compiler)
-               compiler->prepare();
-       for(const string &i: includes)
-               if(Target *hdr = builder.get_vfs().find_header(i.substr(1), compiler, (i[0]=='"' ? local_incpath : incpath)))
-                       add_transitive_dependency(*hdr);
-}
-
-void CSourceFile::modified()
-{
-       includes.clear();
-       trans_depends.clear();
-       find_dependencies();
-}
diff --git a/source/csourcefile.h b/source/csourcefile.h
deleted file mode 100644 (file)
index c57654a..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef CSOURCEFILE_H_
-#define CSOURCEFILE_H_
-
-#include <msp/io/base.h>
-#include "sourcefile.h"
-
-/**
-Represents a C or C++ source file.
-*/
-class CSourceFile: public SourceFile
-{
-protected:
-       std::vector<std::string> includes;
-
-public:
-       CSourceFile(Builder &b, const Msp::FS::Path &p): SourceFile(b, p) { }
-       CSourceFile(Builder &, const Component &, const Msp::FS::Path &);
-
-       const char *get_type() const override { return "CSourceFile"; }
-       const std::vector<std::string> &get_includes() const { return includes; }
-protected:
-       virtual void parse_includes(Msp::IO::Base &);
-       void find_dependencies() override;
-       void modified() override;
-};
-
-#endif
diff --git a/source/customizedtool.cpp b/source/customizedtool.cpp
deleted file mode 100644 (file)
index 9171996..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#include "builder.h"
-#include "customizedtool.h"
-#include "target.h"
-
-using namespace std;
-using namespace Msp;
-
-CustomizedTool::CustomizedTool(Builder &b, const std::string &t, const Architecture &a):
-       CustomizedTool(b.get_toolchain().get_tool(t), a)
-{ }
-
-CustomizedTool::CustomizedTool(Tool &t, const Architecture &a):
-       Tool(t.get_builder(), &a, t.get_tag()),
-       parent(t)
-{
-       input_suffixes = parent.get_input_suffixes();
-       aux_suffixes = parent.get_auxiliary_suffixes();
-       processing_unit = parent.get_processing_unit();
-
-       set_run([this](const Target &tgt){ return parent.run(tgt); });
-}
-
-Target *CustomizedTool::create_source(const Component &c, const FS::Path &p) const
-{
-       return parent.create_source(c, p);
-}
-
-Target *CustomizedTool::create_source(const FS::Path &p) const
-{
-       return parent.create_source(p);
-}
-
-Target *CustomizedTool::create_target(const vector<Target *> &s, const string &a)
-{
-       Target *target = parent.create_target(s, a);
-       target->set_tool(*this);
-       return target;
-}
-
-Target *CustomizedTool::create_install(Target &t) const
-{
-       return parent.create_install(t);
-}
-
-string CustomizedTool::create_build_signature(const BuildInfo &bi) const
-{
-       string sig = Tool::create_build_signature(bi);
-       string parent_sig = parent.create_build_signature(bi);
-       string::size_type comma = parent_sig.find(',');
-       if(comma==string::npos)
-               return sig;
-       else
-               return sig+parent_sig.substr(comma);
-}
-
-void CustomizedTool::do_prepare(ToolData &tool) const
-{
-       parent.prepare(static_cast<Tool &>(tool));
-}
diff --git a/source/customizedtool.h b/source/customizedtool.h
deleted file mode 100644 (file)
index 55c4c6a..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef CUSTOMIZEDTOOL_H_
-#define CUSTOMIZEDTOOL_H_
-
-#include "tool.h"
-
-class CustomizedTool: public Tool
-{
-protected:
-       Tool &parent;
-
-       CustomizedTool(Builder &, const std::string &, const Architecture &);
-       CustomizedTool(Tool &, const Architecture &);
-
-public:
-       const Tool *get_base_tool() const override { return &parent; }
-       Target *create_source(const Component &, const Msp::FS::Path &) const override;
-       Target *create_source(const Msp::FS::Path &) const override;
-       Target *create_target(const std::vector<Target *> &, const std::string & = std::string()) override;
-       Target *create_install(Target &) const override;
-       std::string create_build_signature(const BuildInfo &) const override;
-protected:
-       void do_prepare(ToolData &) const override;
-};
-
-#endif
diff --git a/source/datacollection.cpp b/source/datacollection.cpp
deleted file mode 100644 (file)
index c34c9cc..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <msp/fs/utils.h>
-#include "component.h"
-#include "datacollection.h"
-#include "datatransform.h"
-#include "sourcepackage.h"
-
-using namespace Msp;
-
-DataCollection::DataCollection(Builder &b, const Component &c, DataTransform &s):
-       FileTarget(b, c.get_package(), generate_target_path(c, s.get_path())),
-       source(s)
-{
-       component = &c;
-       add_dependency(source);
-}
-
-Msp::FS::Path DataCollection::generate_target_path(const Component &comp, const Msp::FS::Path &src)
-{
-       return comp.get_package().get_temp_directory()/comp.get_name()/(FS::basepart(FS::basename(src))+".mdc");
-}
-
-void DataCollection::find_dependencies()
-{
-       source.prepare();
-       for(Target *d: source.get_transitive_dependencies())
-               add_dependency(*d);
-}
diff --git a/source/datacollection.h b/source/datacollection.h
deleted file mode 100644 (file)
index aeb270f..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef DATACOLLECTION_H_
-#define DATACOLLECTION_H_
-
-#include "filetarget.h"
-
-class DataTransform;
-
-class DataCollection: public FileTarget
-{
-private:
-       DataTransform &source;
-
-public:
-       DataCollection(Builder &, const Component &, DataTransform &);
-private:
-       static Msp::FS::Path generate_target_path(const Component &, const Msp::FS::Path &);
-
-public:
-       const char *get_type() const override { return "DataCollection"; }
-       DataTransform &get_source() const { return source; }
-
-private:
-       void find_dependencies() override;
-};
-
-#endif
diff --git a/source/datapack.cpp b/source/datapack.cpp
deleted file mode 100644 (file)
index 713120f..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "component.h"
-#include "datapack.h"
-#include "sourcepackage.h"
-
-using namespace std;
-
-DataPack::DataPack(Builder &b, const Component &c, const vector<FileTarget *> &f):
-       FileTarget(b, c.get_package(), generate_target_path(c)),
-       files(f)
-{
-       component = &c;
-       for(FileTarget *t: files)
-               add_dependency(*t);
-
-       install_location = Msp::FS::Path("share")/package->get_name();
-}
-
-Msp::FS::Path DataPack::generate_target_path(const Component &comp)
-{
-       return comp.get_package().get_output_directory()/(comp.get_name()+".mdp");
-}
diff --git a/source/datapack.h b/source/datapack.h
deleted file mode 100644 (file)
index b02922f..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef DATAPACK_H_
-#define DATAPACK_H_
-
-#include "filetarget.h"
-
-class DataPack: public FileTarget
-{
-private:
-       std::vector<FileTarget *> files;
-
-public:
-       DataPack(Builder &, const Component &, const std::vector<FileTarget *> &);
-private:
-       static Msp::FS::Path generate_target_path(const Component &);
-
-public:
-       const char *get_type() const override { return "DataPack"; }
-
-       const std::vector<FileTarget *> &get_files() const { return files; }
-};
-
-#endif
diff --git a/source/datapackcomponent.cpp b/source/datapackcomponent.cpp
deleted file mode 100644 (file)
index 5750287..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#include <msp/fs/utils.h>
-#include "builder.h"
-#include "datapackcomponent.h"
-#include "datasourcefile.h"
-#include "sourcepackage.h"
-#include "tool.h"
-
-using namespace std;
-using namespace Msp;
-
-DataPackComponent::DataPackComponent(SourcePackage &p, const string &n):
-       Component(p, n)
-{ }
-
-void DataPackComponent::create_targets() const
-{
-       Builder &builder = package.get_builder();
-       Tool &dcomp = builder.get_toolchain().get_tool("DATA");
-
-       vector<Target *> files;
-       for(const FS::Path &s: collect_source_files())
-       {
-               string ext = FS::extpart(FS::basename(s));
-               if(ext==".mdt")
-               {
-                       Target *src = dcomp.create_source(*this, s);
-                       files.push_back(dcomp.create_target(*src, "collection"));
-               }
-               else if(Target *tgt = builder.get_vfs().get_target(s))
-                       files.push_back(tgt);
-               else
-                       files.push_back(new DataSourceFile(builder, *this, s));
-       }
-
-       Target *result = dcomp.create_target(files, "pack");
-
-       BuildGraph &build_graph = builder.get_build_graph();
-       build_graph.add_primary_target(*result);
-       if(install)
-               build_graph.add_installed_target(*result);
-}
diff --git a/source/datapackcomponent.h b/source/datapackcomponent.h
deleted file mode 100644 (file)
index 75eda73..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef DATAPACKCOMPONENT_H_
-#define DATAPACKCOMPONENT_H_
-
-#include "component.h"
-
-class DataPackComponent: public Component
-{
-public:
-       DataPackComponent(SourcePackage &, const std::string &);
-
-       void create_targets() const override;
-};
-
-#endif
diff --git a/source/datasourcefile.h b/source/datasourcefile.h
deleted file mode 100644 (file)
index 7518423..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef DATASOURCEFILE_H_
-#define DATASOURCEFILE_H_
-
-#include "sourcefile.h"
-
-class DataSourceFile: public SourceFile
-{
-public:
-       DataSourceFile(Builder &b, const Msp::FS::Path &p): SourceFile(b, p) { }
-       DataSourceFile(Builder &b, const Component &c, const Msp::FS::Path &p): SourceFile(b, c, p) { }
-
-       const char *get_type() const override { return "DataSourceFile"; }
-};
-
-#endif
diff --git a/source/datatool.cpp b/source/datatool.cpp
deleted file mode 100644 (file)
index 1bc629e..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-#include <stdexcept>
-#include <msp/fs/utils.h>
-#include "builder.h"
-#include "component.h"
-#include "datacollection.h"
-#include "datapack.h"
-#include "datatool.h"
-#include "datatransform.h"
-#include "externaltask.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-DataTool::DataTool(Builder &b):
-       Tool(b, "DATA")
-{
-       set_command("mspdatatool");
-       set_run(_run);
-       input_suffixes.push_back(".mdt");
-}
-
-Target *DataTool::create_source(const Component &comp, const FS::Path &path) const
-{
-       return new DataTransform(builder, comp, path);
-}
-
-Target *DataTool::create_target(const vector<Target *> &sources, const string &arg)
-{
-       if(arg=="collection")
-       {
-               if(sources.size()!=1)
-                       throw invalid_argument("DataTool::create_target");
-               DataTransform &source = dynamic_cast<DataTransform &>(*sources.front());
-               DataCollection *coll = new DataCollection(builder, *source.get_component(), source);
-               coll->set_tool(*this);
-               return coll;
-       }
-       else if(arg=="pack")
-       {
-               if(sources.empty())
-                       throw invalid_argument("DataTool::create_target");
-               vector<FileTarget *> files;
-               files.reserve(sources.size());
-               for(Target *t: sources)
-                       files.push_back(&dynamic_cast<FileTarget &>(*t));
-               DataPack *pack = new DataPack(builder, *files.front()->get_component(), files);
-               pack->set_tool(*this);
-               return pack;
-       }
-       else
-               throw invalid_argument("DataTool::create_target");
-}
-
-string DataTool::create_build_signature(const BuildInfo &binfo) const
-{
-       string result = Tool::create_build_signature(binfo);
-       if(binfo.debug || binfo.optimize)
-               result += ',';
-       if(binfo.debug)
-               result += 'g';
-       if(binfo.optimize>0)
-       {
-               result += 'b';
-               if(binfo.optimize>1)
-                       result += 'z';
-       }
-       return result;
-}
-
-Task *DataTool::_run(const Target &tgt)
-{
-       const Tool &tool = *tgt.get_tool();
-       const Component &comp = *tgt.get_component();
-       FS::Path work_dir = comp.get_package().get_source_directory();
-
-       vector<string> argv;
-       argv.push_back(tool.get_executable()->get_path().str());
-
-       argv.push_back("-o");
-       argv.push_back(FS::relative(dynamic_cast<const FileTarget &>(tgt).get_path(), work_dir).str());
-
-       BuildInfo binfo;
-       tgt.collect_build_info(binfo);
-       if(binfo.debug)
-               argv.push_back("-g");
-       if(binfo.optimize>0)
-       {
-               argv.push_back("-b");
-               if(binfo.optimize>1)
-                       argv.push_back("-z");
-       }
-
-       if(const DataCollection *coll = dynamic_cast<const DataCollection *>(&tgt))
-       {
-               argv.push_back("-c");
-               argv.push_back(FS::relative(coll->get_source().get_path(), work_dir).str());
-       }
-       else if(const DataPack *pack = dynamic_cast<const DataPack *>(&tgt))
-       {
-               argv.push_back("-p");
-               for(const FileTarget *f: pack->get_files())
-                       argv.push_back(FS::relative(f->get_path(), work_dir).str());
-       }
-
-       return new ExternalTask(argv, work_dir);
-}
diff --git a/source/datatool.h b/source/datatool.h
deleted file mode 100644 (file)
index 1cddebb..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef DATACOMPILER_H_
-#define DATACOMPILER_H_
-
-#include "tool.h"
-
-class DataTool: public Tool
-{
-public:
-       DataTool(Builder &);
-
-       Target *create_source(const Component &, const Msp::FS::Path &) const override;
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-       std::string create_build_signature(const BuildInfo &) const override;
-
-private:
-       static Task *_run(const Target &);
-};
-
-#endif
diff --git a/source/datatransform.cpp b/source/datatransform.cpp
deleted file mode 100644 (file)
index 01d237b..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#include <msp/fs/dir.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include <msp/strings/regex.h>
-#include "builder.h"
-#include "cache.h"
-#include "component.h"
-#include "datatransform.h"
-#include "file.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-DataTransform::DataTransform(Builder &b, const Component &c, const FS::Path &p):
-       FileTarget(b, c.get_package(), p)
-{
-       component = &c;
-
-       if(FS::Stat st = FS::lstat(FS::dirname(path)))
-               dir_mtime = st.get_modify_time();
-}
-
-void DataTransform::find_dependencies()
-{
-       vector<string> files;
-       Cache &cache = component->get_package().get_cache();
-       const Time::TimeStamp &cache_mtime = cache.get_mtime();
-       if(mtime<cache_mtime && dir_mtime<cache_mtime && cache.has_key(this, "files"))
-               files = cache.get_values(this, "files");
-       else
-       {
-               builder.get_logger().log("files", "Reading imports from %s", path.str());
-               IO::File in(path.str());
-               DataFile::Parser parser(in, path.str());
-
-               vector<string> dir_files;
-               while(!in.eof())
-               {
-                       DataFile::Statement st = parser.parse();
-                       if(st.keyword=="for_each")
-                       {
-                               // There's bound to be at least one file: the transform itself
-                               if(dir_files.empty())
-                               {
-                                       FS::Path dir = FS::dirname(path);
-                                       builder.get_logger().log("files", "Traversing %s", dir.str());
-                                       dir_files = list_files(dir);
-                               }
-
-                               for(const DataFile::Value &a: st.args)
-                               {
-                                       Regex re(a.get<string>());
-                                       for(const string &f: dir_files)
-                                               if(re.match(f))
-                                                       files.push_back(f);
-                               }
-                       }
-                       else if(st.keyword=="file" && st.args.size()==1)
-                               files.push_back(st.args.front().get<string>());
-               }
-
-               cache.set_values(this, "files", files);
-       }
-
-       for(const string &f: files)
-       {
-               FS::Path file_path = FS::dirname(path)/f;
-               if(Target *tgt = builder.get_vfs().get_target(file_path))
-                       add_transitive_dependency(*tgt);
-               else
-                       add_transitive_dependency(*new File(builder, *package, file_path));
-       }
-}
diff --git a/source/datatransform.h b/source/datatransform.h
deleted file mode 100644 (file)
index f024973..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef DATATRANSFORM_H_
-#define DATATRANSFORM_H_
-
-#include "filetarget.h"
-
-class DataTransform: public FileTarget
-{
-private:
-       Msp::Time::TimeStamp dir_mtime;
-
-public:
-       DataTransform(Builder &, const Component &, const Msp::FS::Path &);
-
-       const char *get_type() const override { return "DataTransform"; }
-
-private:
-       void find_dependencies() override;
-};
-
-#endif
diff --git a/source/executable.cpp b/source/executable.cpp
deleted file mode 100644 (file)
index aa0d8fb..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "builder.h"
-#include "component.h"
-#include "executable.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-Executable::Executable(Builder &b, const Component &c, const vector<ObjectFile *> &objs):
-       Binary(b, c, b.get_current_arch().create_filename<Executable>(c.get_name()), objs)
-{
-       install_location = "bin";
-}
diff --git a/source/executable.h b/source/executable.h
deleted file mode 100644 (file)
index 5728e2f..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef EXECUTABLE_H_
-#define EXECUTABLE_H_
-
-#include "binary.h"
-
-class Executable: public Binary
-{
-public:
-       Executable(Builder &b, const Msp::FS::Path &p): Binary(b, p) { }
-       Executable(Builder &, const Component &, const std::vector<ObjectFile *> &);
-
-       const char *get_type() const override { return "Executable"; }
-};
-
-#endif
diff --git a/source/exportdefinitions.cpp b/source/exportdefinitions.cpp
deleted file mode 100644 (file)
index 7edf759..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "component.h"
-#include "exportdefinitions.h"
-#include "objectfile.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-ExportDefinitions::ExportDefinitions(Builder &b, const Component &c, const vector<ObjectFile *> &objs):
-       FileTarget(b, c.get_package(), generate_target_path(c))
-{
-       component = &c;
-       for(ObjectFile *o: objs)
-               add_dependency(*o);
-}
-
-FS::Path ExportDefinitions::generate_target_path(const Component &comp)
-{
-       return comp.get_package().get_temp_directory()/comp.get_name()/(comp.get_name()+".def");
-}
diff --git a/source/exportdefinitions.h b/source/exportdefinitions.h
deleted file mode 100644 (file)
index b8631ee..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef EXPORTDEFINITIONS_H_
-#define EXPORTDEFINITIONS_H_
-
-#include "filetarget.h"
-
-class ObjectFile;
-
-/**
-An export definition file for a shared library.  Only used on Windows.
-*/
-class ExportDefinitions: public FileTarget
-{
-public:
-       ExportDefinitions(Builder &, const Component &, const std::vector<ObjectFile *> &);
-private:
-       static Msp::FS::Path generate_target_path(const Component &);
-
-public:
-       const char *get_type() const override { return "ExportDefinitions"; }
-};
-
-#endif
diff --git a/source/externaltask.cpp b/source/externaltask.cpp
deleted file mode 100644 (file)
index 37a36d3..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-#include <cstdlib>
-#include <msp/fs/dir.h>
-#include <msp/io/console.h>
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include <msp/time/timedelta.h>
-#include "externaltask.h"
-
-using namespace std;
-using namespace Msp;
-
-ExternalTask::ExternalTask(const Arguments &a, const FS::Path &wd):
-       argv(a),
-       work_dir(wd)
-{
-       if(argv.empty())
-               throw invalid_argument("ExternalTask::ExternalTask");
-}
-
-ExternalTask::~ExternalTask()
-{
-       delete capture_pipe;
-}
-
-string ExternalTask::get_command() const
-{
-       string cmd;
-       for(const string &a: argv)
-       {
-               if(!cmd.empty())
-                       cmd += ' ';
-
-               for(char c: a)
-               {
-                       if(c=='"' || c=='\'' || c==' ' || c=='\\' || c=='&')
-                               cmd += '\\';
-                       cmd += c;
-               }
-       }
-
-       if(stdin_action==REDIRECT)
-       {
-               cmd += " <";
-               cmd += stdin_file.str();
-       }
-
-       if(stdout_action==REDIRECT)
-       {
-               cmd += " >";
-               cmd += stdout_file.str();
-       }
-
-       return cmd;
-}
-
-void ExternalTask::start()
-{
-       IO::File *devnull = 0;
-       IO::File *infile = 0;
-       IO::File *outfile = 0;
-
-       prepare();
-
-       process = new Process;
-
-       if(stdin_action==IGNORE || stdout_action==IGNORE || stderr_action==IGNORE)
-       {
-#ifdef _WIN32
-               devnull = new IO::File("nul", IO::M_RDWR);
-#else
-               devnull = new IO::File("/dev/null", IO::M_RDWR);
-#endif
-               if(stdin_action==IGNORE)
-                       process->redirect_cin(*devnull);
-               if(stdout_action==IGNORE)
-                       process->redirect_cout(*devnull);
-               if(stderr_action==IGNORE)
-                       process->redirect_cerr(*devnull);
-       }
-
-       if(stdout_action==REDIRECT)
-       {
-               outfile = new IO::File((work_dir/stdout_file).str(), IO::M_WRITE);
-               process->redirect_cout(*outfile);
-       }
-
-       if(stdout_action==CAPTURE || stderr_action==CAPTURE)
-       {
-               capture_pipe = new IO::Pipe;
-               if(stdout_action==CAPTURE)
-                       process->redirect_cout(*capture_pipe);
-               if(stderr_action==CAPTURE)
-                       process->redirect_cerr(*capture_pipe);
-       }
-
-       if(stdin_action==REDIRECT)
-       {
-               infile = new IO::File((work_dir/stdin_file).str());
-               process->redirect_cin(*infile);
-       }
-
-       if(!work_dir.empty())
-               process->set_working_directory(work_dir);
-
-       Process::Arguments args(argv.begin()+1, argv.end());
-       process->execute(argv.front(), args);
-       if(capture_pipe)
-               capture_pipe->set_mode(IO::M_READ);
-
-       delete devnull;
-       delete infile;
-       delete outfile;
-}
-
-Task::Status ExternalTask::check()
-{
-       return do_wait(false);
-}
-
-Task::Status ExternalTask::wait()
-{
-       return do_wait(true);
-}
-
-Task::Status ExternalTask::do_wait(bool block)
-{
-       while(process)
-       {
-               if(process->wait(block && !capture_pipe))
-               {
-                       exit_code = process->get_exit_code();
-                       delete process;
-                       process = 0;
-               }
-
-               // Do this after waiting to avoid a race condition
-               while(capture_pipe && IO::poll(*capture_pipe, IO::P_INPUT, 10*Time::msec))
-               {
-                       char buf[1024];
-                       unsigned len = capture_pipe->read(buf, sizeof(buf));
-                       if(len)
-                               output.append(buf, len);
-                       else
-                               break;
-               }
-
-               if(process)
-               {
-                       if(!block)
-                               return RUNNING;
-               }
-               else
-                       signal_finished.emit(!exit_code);
-       }
-
-       return exit_code ? ERROR : SUCCESS;
-}
-
-void ExternalTask::set_stdin(const FS::Path &f)
-{
-       stdin_action = REDIRECT;
-       stdin_file = f;
-}
-
-void ExternalTask::set_stdout(StreamAction a)
-{
-       if(a==REDIRECT)
-               throw invalid_argument("ExternalTask::set_stdout");
-       stdout_action = a;
-}
-
-void ExternalTask::set_stdout(const FS::Path &f)
-{
-       stdout_action = REDIRECT;
-       stdout_file = f;
-}
-
-void ExternalTask::set_stderr(StreamAction a)
-{
-       if(a==REDIRECT)
-               throw invalid_argument("ExternalTask::set_stdout");
-       stderr_action = a;
-}
-
-string ExternalTask::run_and_capture_output(const Arguments &argv, const FS::Path &wd, bool capture_stderr)
-{
-       ExternalTask task(argv, wd);
-       task.stdin_action = IGNORE;
-       task.set_stdout(CAPTURE);
-       task.set_stderr(capture_stderr ? CAPTURE : IGNORE);
-       task.start();
-       if(task.wait()!=SUCCESS)
-               throw runtime_error(format("%s failed", argv.front()));
-       return task.get_output();
-}
diff --git a/source/externaltask.h b/source/externaltask.h
deleted file mode 100644 (file)
index 9b9bc43..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef EXTERNALTASK_H_
-#define EXTERNALTASK_H_
-
-#include <string>
-#include <vector>
-#include <msp/core/process.h>
-#include <msp/fs/path.h>
-#include <msp/io/pipe.h>
-#include "task.h"
-
-/**
-Runs an external command.  A zero exit status is translated to a SUCCESS status
-for the task, and anything else is treated as an error.  Output can optionally
-be captured.
-*/
-class ExternalTask: public Task
-{
-public:
-       enum StreamAction
-       {
-               PASSTHROUGH,  //< Do not touch the stream
-               CAPTURE,      //< Capture the stream
-               REDIRECT,     //< Redirect the stream to/from a file
-               IGNORE        //< Redirect the stream to oblivion
-       };
-
-       using Arguments = Msp::Process::Arguments;
-
-private:
-       Arguments argv;
-       Msp::FS::Path work_dir;
-       Msp::Process *process = 0;
-       int exit_code = -1;
-       StreamAction stdin_action = PASSTHROUGH;
-       Msp::FS::Path stdin_file;
-       StreamAction stdout_action = PASSTHROUGH;
-       Msp::FS::Path stdout_file;
-       StreamAction stderr_action = PASSTHROUGH;
-       Msp::IO::Pipe *capture_pipe = 0;
-       std::string output;
-
-public:
-       /** Creates an ExternalTask with an argument array and an optional working
-       directory.  The first element of the argument array should be the command
-       name.  If the working directory is not specified, no chdir is done. */
-       ExternalTask(const Arguments &, const Msp::FS::Path & = Msp::FS::Path());
-
-       ~ExternalTask();
-
-       std::string get_command() const override;
-       void start() override;
-       Status check() override;
-       Status wait() override;
-private:
-       Status do_wait(bool);
-
-public:
-       /// Redirect stdin from a file.  Has no effect after the task is started.
-       void set_stdin(const Msp::FS::Path &);
-
-       /// Sets destination for stdout.  Has no effect after the task is started.
-       void set_stdout(StreamAction);
-
-       /// Redirect stdout to a file.  Has no effect after the task is started.
-       void set_stdout(const Msp::FS::Path &);
-
-       /// Sets destination for stderr.  Has no effect after the task is started.
-       void set_stderr(StreamAction);
-
-       /** Returns captured output, if any.  This may be called while the task is
-       still running, but it will always return all output. */
-       const std::string &get_output() const { return output; }
-
-       /** Executes a command and captures its output.  If the command exits with
-       a nonzero status, an exception is thrown. */
-       static std::string run_and_capture_output(const Arguments &, const Msp::FS::Path & = Msp::FS::Path(), bool = false);
-};
-
-#endif
diff --git a/source/feature.cpp b/source/feature.cpp
deleted file mode 100644 (file)
index 2518377..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "feature.h"
-
-using namespace std;
-using namespace Msp;
-
-Feature::Loader::Loader(Feature &f):
-       Msp::DataFile::ObjectLoader<Feature>(f)
-{
-       add("choice",      &Loader::choice);
-       add("description", &Feature::description);
-       add("default",     &Feature::default_value);
-       add("export",      &Feature::exported);
-}
-
-void Feature::Loader::choice(const string &c)
-{
-       if(obj.choices.empty())
-               obj.default_value = c;
-       obj.choices.push_back(c);
-}
diff --git a/source/feature.h b/source/feature.h
deleted file mode 100644 (file)
index 0dca1f5..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef FEATURE_H_
-#define FEATURE_H_
-
-#include <msp/datafile/objectloader.h>
-
-struct Feature
-{
-       class Loader: public Msp::DataFile::ObjectLoader<Feature>
-       {
-       public:
-               Loader(Feature &);
-
-       private:
-               void choice(const std::string &);
-       };
-
-       std::string name;
-       std::string description;
-       std::string default_value = "no";
-       std::vector<std::string> choices;
-       bool exported = false;
-
-       Feature(const std::string &n): name(n) { }
-};
-
-#endif
diff --git a/source/file.h b/source/file.h
deleted file mode 100644 (file)
index 42d73b8..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef FILE_H_
-#define FILE_H_
-
-#include "filetarget.h"
-
-/**
-Just an arbitrary file.  No special meaning attached.
-*/
-class File: public FileTarget
-{
-public:
-       File(Builder &b, const Msp::FS::Path &t): FileTarget(b, t) { }
-       File(Builder &b, const SourcePackage &p, const Msp::FS::Path &t): FileTarget(b, p, t) { }
-
-       const char *get_type() const override { return "File"; }
-};
-
-#endif
diff --git a/source/filetarget.cpp b/source/filetarget.cpp
deleted file mode 100644 (file)
index a2bd3f0..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include <msp/strings/utils.h>
-#include <msp/time/utils.h>
-#include "builder.h"
-#include "filetarget.h"
-#include "sourcepackage.h"
-#include "task.h"
-#include "tool.h"
-
-using namespace std;
-using namespace Msp;
-
-FileTarget::FileTarget(Builder &b, const SourcePackage *p, const FS::Path &a):
-       Target(b, generate_name(b, p, a)),
-       path(a)
-{
-       package = p;
-
-       builder.get_vfs().register_path(path, this);
-
-       stat();
-}
-
-void FileTarget::stat()
-{
-       if(FS::Stat st = FS::lstat(path))
-       {
-               mtime = st.get_modify_time();
-               size = st.get_size();
-       }
-}
-
-string FileTarget::generate_name(Builder &builder, const SourcePackage *pkg, const FS::Path &path)
-{
-       if(pkg && FS::descendant_depth(path, pkg->get_source_directory())>=0)
-       {
-               FS::Path relpath = FS::relative(path, pkg->get_source_directory());
-               return format("<%s>%s", pkg->get_name(), relpath.str().substr(1));
-       }
-       else if(FS::descendant_depth(path, builder.get_prefix())>=0)
-       {
-               FS::Path relpath = FS::relative(path, builder.get_prefix());
-               return "<prefix>"+relpath.str().substr(1);
-       }
-
-       return path.str();
-}
-
-void FileTarget::touch()
-{
-       mtime = Time::now();
-       modified();
-       signal_bubble_rebuild.emit();
-}
-
-void FileTarget::check_rebuild()
-{
-       if(!tool || needs_rebuild())
-               return;
-
-       if(!mtime)
-               mark_rebuild("Does not exist");
-       else
-       {
-               for(Target *d: depends)
-               {
-                       FileTarget *ft = dynamic_cast<FileTarget *>(d);
-                       if(ft && ft->get_mtime()>mtime)
-                               mark_rebuild(d->get_name()+" has changed");
-                       else if(d->needs_rebuild())
-                               mark_rebuild(d->get_name()+" needs rebuilding");
-                       if(needs_rebuild())
-                               break;
-               }
-       }
-
-       if(!needs_rebuild())
-       {
-               // Some side effects might not exist
-               auto i = find_if(side_effects, [](const Target *s){ return s->needs_rebuild(); });
-               if(i!=side_effects.end())
-                       mark_rebuild((*i)->get_name()+" needs rebuilding");
-       }
-
-       if(!needs_rebuild() && package)
-       {
-               if(package->get_config().get_mtime()>mtime)
-                       mark_rebuild("Package options changed");
-
-               if(tool->get_executable())
-               {
-                       string build_sig = create_build_signature();
-                       if(package->get_cache().has_key(this, "build_sig"))
-                       {
-                               if(package->get_cache().get_value(this, "build_sig")!=build_sig)
-                                       mark_rebuild("Build signature changed");
-                       }
-               }
-       }
-}
-
-string FileTarget::create_build_signature() const
-{
-       if(!package)
-               return string();
-
-       const BuildInfo &binfo = (component ? component->get_build_info() : package->get_build_info());
-       vector<string> sigs;
-
-       if(arch_in_build_sig)
-               if(const Architecture *arch = tool->get_architecture())
-                       sigs.push_back(arch->get_name());
-
-       sigs.push_back(tool->create_build_signature(binfo));
-
-       if(nested_build_sig && component)
-       {
-               vector<const Tool *> seen_tools;
-               vector<string> tool_sigs;
-               for(Target *d: depends)
-                       if(const Tool *t = d->get_tool())
-                               if(d->get_component()==component && !any_equals(seen_tools, t))
-                               {
-                                       seen_tools.push_back(t);
-                                       tool_sigs.push_back(t->create_build_signature(binfo));
-                               }
-
-               sort(tool_sigs);
-               sigs.insert(sigs.end(), make_move_iterator(tool_sigs.begin()), make_move_iterator(tool_sigs.end()));
-       }
-
-       return join(sigs.begin(), sigs.end(), ";");
-}
-
-void FileTarget::build(Task &task)
-{
-       task.add_file(path);
-       task.set_unlink(true);
-}
-
-void FileTarget::build_finished(bool success)
-{
-       if(success)
-       {
-               stat();
-               if(package)
-               {
-                       string build_sig = create_build_signature();
-                       if(!build_sig.empty())
-                               package->get_cache().set_value(this, "build_sig", build_sig);
-               }
-       }
-
-       Target::build_finished(success);
-}
-
-void FileTarget::clean()
-{
-       if(mtime)
-       {
-               FS::unlink(path);
-               mtime = Time::TimeStamp();
-               size = 0;
-               check_rebuild();
-       }
-}
diff --git a/source/filetarget.h b/source/filetarget.h
deleted file mode 100644 (file)
index da165d0..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef FILETARGET_H_
-#define FILETARGET_H_
-
-#include <msp/fs/path.h>
-#include "target.h"
-
-/**
-An intermediate base class for targets that represent files.  Almost all target
-classes are derived from this.
-*/
-class FileTarget: public Target
-{
-protected:
-       Msp::FS::Path path;
-       Msp::Time::TimeStamp mtime;
-       unsigned size = 0;
-       Msp::FS::Path install_location;
-       std::string install_filename;
-       bool nested_build_sig = false;
-       bool arch_in_build_sig = false;
-
-       FileTarget(Builder &b, const Msp::FS::Path &a): FileTarget(b, 0, a) { }
-       FileTarget(Builder &b, const SourcePackage &p, const Msp::FS::Path &a): FileTarget(b, &p, a) { }
-private:
-       FileTarget(Builder &, const SourcePackage *, const Msp::FS::Path &);
-       void stat();
-       static std::string generate_name(Builder &, const SourcePackage *, const Msp::FS::Path &);
-
-public:
-       const Msp::FS::Path &get_path() const { return path; }
-       const Msp::Time::TimeStamp &get_mtime() const { return mtime; }
-       unsigned get_size() const { return size; }
-
-       bool is_installable() const { return !install_location.empty(); }
-       const Msp::FS::Path &get_install_location() const { return install_location; }
-       const std::string &get_install_filename() const { return install_filename; }
-
-       /// Changes the mtime of the target to the current time.
-       void touch();
-
-protected:
-       void check_rebuild() override;
-
-       virtual std::string create_build_signature() const;
-
-       void build(Task &) override;
-
-       void build_finished(bool) override;
-
-public:
-       void clean() override;
-};
-
-#endif
diff --git a/source/gnuarchiver.cpp b/source/gnuarchiver.cpp
deleted file mode 100644 (file)
index dc0b784..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#include <msp/fs/dir.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include <stdexcept>
-#include "builder.h"
-#include "component.h"
-#include "gnuarchiver.h"
-#include "objectfile.h"
-#include "sourcepackage.h"
-#include "staticlibrary.h"
-
-using namespace std;
-using namespace Msp;
-
-GnuArchiver::GnuArchiver(Builder &b, const Architecture &a):
-       Tool(b, &a, "AR")
-{
-       set_command("ar", true);
-       input_suffixes.push_back(".o");
-       processing_unit = COMPONENT;
-       set_run_external(_run);
-}
-
-Target *GnuArchiver::create_target(const vector<Target *> &sources, const string &)
-{
-       if(sources.empty())
-               throw invalid_argument("GnuArchiver::create_target");
-
-       vector<ObjectFile *> objs;
-       objs.reserve(sources.size());
-       for(Target *s: sources)
-               objs.push_back(&dynamic_cast<ObjectFile &>(*s));
-
-       const Component &comp = *objs.front()->get_component();
-       StaticLibrary *lib = new StaticLibrary(builder, comp, objs);
-       lib->set_tool(*this);
-       return lib;
-}
-
-ExternalTask::Arguments GnuArchiver::_run(const StaticLibrary &lib, FS::Path &work_dir)
-{
-       const Tool &tool = *lib.get_tool();
-
-       vector<string> argv;
-       argv.push_back(tool.get_executable()->get_path().str());
-       argv.push_back("rc");
-
-       argv.push_back(relative(lib.get_path(), work_dir).str());
-
-       for(Target *d: lib.get_dependencies())
-               if(ObjectFile *obj = dynamic_cast<ObjectFile *>(d))
-                       argv.push_back(relative(obj->get_path(), work_dir).str());
-
-       return argv;
-}
diff --git a/source/gnuarchiver.h b/source/gnuarchiver.h
deleted file mode 100644 (file)
index 4305827..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef GNUARCHIVER_H_
-#define GNUARCHIVER_H_
-
-#include "tool.h"
-
-class StaticLibrary;
-
-class GnuArchiver: public Tool
-{
-public:
-       GnuArchiver(Builder &, const Architecture &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static ExternalTask::Arguments _run(const StaticLibrary &, Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/gnucompiler.cpp b/source/gnucompiler.cpp
deleted file mode 100644 (file)
index 3236631..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-#include <msp/core/maputils.h>
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include <msp/strings/utils.h>
-#include "architecture.h"
-#include "builder.h"
-#include "component.h"
-#include "csourcefile.h"
-#include "gnucompiler.h"
-#include "objcsourcefile.h"
-#include "objectfile.h"
-#include "sourcefile.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace {
-
-const char *cpus[] =
-{
-       "athlonxp", "athlon-xp",
-       "armv7a",   "armv7-a",
-       0
-};
-
-}
-
-GnuCompiler::GnuCompiler(Builder &b, const Architecture &a, const string &t):
-       Tool(b, &a, t)
-{
-       if(tag=="CC")
-       {
-               input_suffixes.push_back(".c");
-               aux_suffixes.push_back(".h");
-       }
-       else if(tag=="CXX")
-       {
-               input_suffixes.push_back(".cpp");
-               input_suffixes.push_back(".cc");
-               aux_suffixes.push_back(".hpp");
-       }
-       else if(tag=="OBJC")
-       {
-               input_suffixes.push_back(".m");
-               build_info.libs.push_back("objc");
-       }
-       else
-               throw invalid_argument("GnuCompiler::GnuCompiler");
-
-       set_command((tag=="CXX" ? "g++" : "gcc"), true);
-       set_run_external(_run);
-}
-
-Target *GnuCompiler::create_source(const Component &comp, const FS::Path &path) const
-{
-       if(tag=="OBJC")
-               return new ObjCSourceFile(builder, comp, path);
-       else
-               return new CSourceFile(builder, comp, path);
-}
-
-Target *GnuCompiler::create_source(const FS::Path &path) const
-{
-       if(tag=="OBJC")
-               return new ObjCSourceFile(builder, path);
-       else
-               return new CSourceFile(builder, path);
-}
-
-Target *GnuCompiler::create_target(const vector<Target *> &sources, const string &)
-{
-       if(sources.size()!=1)
-               throw invalid_argument("GnuCompiler::create_target");
-       SourceFile &source = dynamic_cast<SourceFile &>(*sources.front());
-       ObjectFile *obj = new ObjectFile(builder, *source.get_component(), source);
-       obj->set_tool(*this);
-       return obj;
-}
-
-string GnuCompiler::create_build_signature(const BuildInfo &binfo) const
-{
-       if(!executable)
-               return string();
-
-       string result = Tool::create_build_signature(binfo);
-       if(!architecture->get_cpu().empty())
-       {
-               result += ",m";
-               result += architecture->get_cpu();
-       }
-       if(binfo.debug || binfo.optimize)
-               result += ',';
-       if(binfo.debug)
-               result += 'g';
-       if(binfo.optimize)
-       {
-               result += 'O';
-               result += (binfo.optimize>0 ? '0'+binfo.optimize : 's');
-       }
-       return result;
-}
-
-void GnuCompiler::do_prepare(ToolData &tool) const
-{
-       tool.extra_data = 0U;
-       prepare_syspath(tool);
-       prepare_version(tool);
-
-       if(tag=="CXX")
-               tool.build_info.libs.push_back("stdc++");
-}
-
-void GnuCompiler::prepare_syspath(ToolData &tool) const
-{
-       bool path_found = false;
-       const FS::Path &sysroot = tool.build_info.sysroot;
-       const std::string &tool_tag = static_cast<Tool &>(tool).get_tag();
-
-       const FileTarget *exe = static_cast<Tool &>(tool).get_executable();
-       if(exe)
-       {
-               ExternalTask::Arguments argv;
-               argv.push_back(exe->get_path().str());
-               argv.push_back("-Wp,-v");
-               argv.push_back("-E");
-               if(tag=="CXX")
-                       argv.push_back("-xc++");
-               if(!sysroot.empty())
-                       argv.push_back("--sysroot="+sysroot.str());
-               argv.push_back("-");
-
-               builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
-               try
-               {
-                       string output = ExternalTask::run_and_capture_output(argv, FS::Path(), true);
-                       string::size_type start = 0;
-                       bool record_path = false;
-                       while(start<output.size())
-                       {
-                               string::size_type newline = output.find('\n', start);
-                               if(!output.compare(start, 34, "#include <...> search starts here:"))
-                               {
-                                       record_path = true;
-                                       path_found = true;
-                               }
-                               else if(!output.compare(start, 19, "End of search list."))
-                                       record_path = false;
-                               else if(record_path)
-                               {
-                                       FS::Path path = strip(output.substr(start, newline-start));
-                                       builder.get_logger().log("tools", "Got %s system path: %s", tool_tag, path);
-                                       tool.system_path.push_back(path);
-                               }
-                               start = newline+1;
-                       }
-               }
-               catch(const runtime_error &)
-               { }
-       }
-
-       if(!path_found)
-       {
-               builder.get_logger().log("tools", "No %s system path found, using defaults", tool_tag);
-               const Architecture &arch = *static_cast<Tool &>(tool).get_architecture();
-               if(!sysroot.empty())
-                       tool.system_path.push_back(sysroot/"usr/include");
-               else if(arch.is_native())
-                       tool.system_path.push_back("/usr/include");
-               else
-                       tool.system_path.push_back(format("/usr/%s/include", arch.get_cross_prefix()));
-       }
-}
-
-void GnuCompiler::prepare_version(ToolData &tool) const
-{
-       const FileTarget *exe = static_cast<Tool &>(tool).get_executable();
-       if(!exe)
-               return;
-
-       string exe_path = exe->get_path().str();
-       unsigned version = query_version(exe_path, "-dumpversion");
-       if(version>=0x70000)
-       {
-               unsigned v = query_version(exe_path, "-dumpfullversion");
-               if(v)
-                       version = v;
-       }
-       tool.extra_data = version;
-       builder.get_logger().log("tools", "%s version is %d.%d.%d", FS::basename(exe->get_path()), version>>16, (version>>8)&0xFF, version&0xFF);
-}
-
-unsigned GnuCompiler::query_version(const string &exe_path, const string &arg) const
-{
-       ExternalTask::Arguments argv;
-       argv.push_back(exe_path);
-       argv.push_back(arg);
-
-       builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
-       unsigned ver = 0;
-       try
-       {
-               string version_str = strip(ExternalTask::run_and_capture_output(argv));
-
-               vector<string> version_parts = split(version_str, '.');
-               for(unsigned i=0; (i<3 && i<version_parts.size()); ++i)
-                       ver |= lexical_cast<unsigned>(version_parts[i])<<(16-8*i);
-       }
-       catch(const runtime_error &)
-       { }
-
-       return ver;
-}
-
-ExternalTask::Arguments GnuCompiler::_run(const ObjectFile &object, FS::Path &work_dir)
-{
-       const Tool &tool = *object.get_tool();
-       const Architecture &arch = *tool.get_architecture();
-
-       ExternalTask::Arguments argv;
-       argv.push_back(tool.get_executable()->get_path().str());
-       argv.push_back("-c");
-
-       BuildInfo binfo;
-       object.collect_build_info(binfo);
-
-       const std::string &tool_tag = tool.get_tag();
-       string tag_for_std = (tool_tag=="OBJC" ? "CC" : tool_tag);
-       if(binfo.standards.count(tag_for_std))
-               argv.push_back("-std="+get_item(binfo.standards, tag_for_std).str());
-       if(tool_tag=="OBJC" && binfo.standards.count(tool_tag))
-               argv.push_back("-fobjc-std="+get_item(binfo.standards, tool_tag).str());
-
-       if(binfo.warning_level>=1)
-       {
-               argv.push_back("-Wall");
-               if(binfo.warning_level>=2)
-               {
-                       argv.push_back("-Wextra");
-                       argv.push_back("-Wundef");
-                       unsigned version = tool.get_extra_data();
-                       if(version>=0x80000)
-                               argv.push_back("-Wno-cast-function-type");
-               }
-               if(binfo.warning_level>=3)
-               {
-                       argv.push_back("-pedantic");
-                       argv.push_back("-Wno-long-long");
-                       argv.push_back("-Wshadow");
-                       if(tool_tag=="CC")
-                       {
-                               argv.push_back("-Wc++-compat");
-                               argv.push_back("-Wstrict-prototypes");
-                       }
-               }
-               if(binfo.warning_level>=4)
-               {
-                       // Some truly paranoid warnings
-                       argv.push_back("-Wstrict-overflow=4");
-                       argv.push_back("-Wfloat-equal");
-                       argv.push_back("-Wconversion");
-                       argv.push_back("-Wwrite-strings");
-                       argv.push_back("-Winline");
-               }
-               if(binfo.fatal_warnings)
-               {
-                       argv.push_back("-Werror");
-                       argv.push_back("-Wno-error=deprecated-declarations");
-               }
-       }
-
-       const FS::Path &sysroot = binfo.sysroot;
-       if(!sysroot.empty())
-               argv.push_back("--sysroot="+sysroot.str());
-       for(const FS::Path &p: binfo.local_incpath)
-       {
-               argv.push_back("-iquote");
-               argv.push_back(p.str());
-       }
-       for(const FS::Path &p: binfo.incpath)
-               argv.push_back("-I"+p.str());
-
-       for(const auto &kvp: binfo.defines)
-       {
-               if(kvp.second.empty())
-                       argv.push_back(format("-D%s", kvp.first));
-               else
-                       argv.push_back(format("-D%s=%s", kvp.first, kvp.second));
-       }
-
-       if(binfo.debug)
-               argv.push_back("-ggdb");
-       if(binfo.optimize)
-       {
-               if(binfo.optimize<0)
-                       argv.push_back("-Os");
-               else
-                       argv.push_back(format("-O%d", binfo.optimize));
-               if(binfo.debug)
-                       argv.push_back("-fno-omit-frame-pointer");
-       }
-       if(binfo.threads && arch.get_system()!="windows" && arch.get_system()!="darwin")
-               argv.push_back("-pthread");
-       if(object.is_used_in_shared_library() && arch.get_system()!="windows")
-               argv.push_back("-fPIC");
-
-       if((arch.get_type()=="x86" || arch.get_type()=="ppc") && !arch.is_native())
-               argv.push_back(format("-m%d", arch.get_bits()));
-
-       string cpu = arch.get_cpu();
-       if(!cpu.empty())
-       {
-               for(unsigned i=0; cpus[i]; i+=2)
-                       if(cpu==cpus[i])
-                       {
-                               cpu = cpus[i+1];
-                               break;
-                       }
-               argv.push_back("-march="+cpu);
-       }
-
-       if(!arch.get_fpu().empty())
-       {
-               if(arch.get_type()=="x86")
-               {
-                       if(arch.get_fpu()=="387")
-                               argv.push_back("-mfpmath=387");
-                       else if(!arch.get_fpu().compare(0, 3, "sse"))
-                               argv.push_back("-mfpmath=sse");
-
-                       if(arch.get_fpu()=="sse")
-                               argv.push_back("-msse2");
-                       else if(arch.get_fpu()=="sse3")
-                               argv.push_back("-msse3");
-                       else if(arch.get_fpu()=="sse4.1")
-                               argv.push_back("-msse4.1");
-               }
-               else if(arch.get_type()=="arm")
-               {
-                       argv.push_back("-mfpu="+arch.get_fpu());
-                       argv.push_back("-mfloat-abi=softfp");
-               }
-       }
-
-       FS::Path obj_path = object.get_path();
-       FS::Path src_path = object.get_source().get_path();
-
-       argv.push_back("-o");
-       argv.push_back(relative(obj_path, work_dir).str());
-       argv.push_back(relative(src_path, work_dir).str());
-
-       return argv;
-}
diff --git a/source/gnucompiler.h b/source/gnucompiler.h
deleted file mode 100644 (file)
index b0d0ec9..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef GNUCOMPILER_H_
-#define GNUCOMPILER_H_
-
-#include "tool.h"
-
-class ObjectFile;
-
-/**
-Common base class for GNU compilers.  Turns SourceFiles into ObjectFiles.
-
-Since invocation is mostly the same for all language frontends, most of the
-logic is here and the individual tools only handle creating source files of
-appropriate type.
-*/
-class GnuCompiler: public Tool
-{
-public:
-       GnuCompiler(Builder &, const Architecture &, const std::string &);
-
-       Target *create_source(const Component &, const Msp::FS::Path &) const override;
-       Target *create_source(const Msp::FS::Path &) const override;
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-       std::string create_build_signature(const BuildInfo &) const override;
-protected:
-       void do_prepare(ToolData &) const override;
-       void prepare_syspath(ToolData &) const;
-       void prepare_version(ToolData &) const;
-       unsigned query_version(const std::string &, const std::string &) const;
-
-private:
-       static ExternalTask::Arguments _run(const ObjectFile &, Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/gnulinker.cpp b/source/gnulinker.cpp
deleted file mode 100644 (file)
index b726dd5..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-#include <stdexcept>
-#include <vector>
-#include <msp/core/algorithm.h>
-#include <msp/fs/dir.h>
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include <msp/strings/utils.h>
-#include "builder.h"
-#include "component.h"
-#include "executable.h"
-#include "exportdefinitions.h"
-#include "gnucompiler.h"
-#include "gnulinker.h"
-#include "importlibrary.h"
-#include "installedfile.h"
-#include "objectfile.h"
-#include "sharedlibrary.h"
-#include "sourcepackage.h"
-#include "staticlibrary.h"
-
-using namespace std;
-using namespace Msp;
-
-GnuLinker::GnuLinker(Builder &b, const Architecture &a):
-       Tool(b, &a, "LINK")
-{
-       input_suffixes.push_back(".o");
-       input_suffixes.push_back(".a");
-
-       processing_unit = COMPONENT;
-
-       set_command("gcc", true);
-       set_run_external(_run);
-}
-
-Target *GnuLinker::create_target(const vector<Target *> &sources, const string &arg)
-{
-       if(sources.empty())
-               throw invalid_argument("GnuLinker::create_target");
-       vector<ObjectFile *> objs;
-       objs.reserve(sources.size());
-       for(Target *s: sources)
-               objs.push_back(&dynamic_cast<ObjectFile &>(*s));
-
-       const Component &comp = *objs.front()->get_component();
-       Binary *bin = 0;
-       if(arg=="shared")
-       {
-               SharedLibrary *shlib = new SharedLibrary(builder, comp, objs);
-               if(architecture->get_system()=="windows")
-               {
-                       Tool &dlltool = builder.get_toolchain().get_tool("DLL");
-                       dlltool.create_target(*shlib);
-               }
-               bin = shlib;
-       }
-       else
-               bin = new Executable(builder, comp, objs);
-       bin->set_tool(*this);
-       return bin;
-}
-
-Target *GnuLinker::create_install(Target &target) const
-{
-       if(SharedLibrary *shlib = dynamic_cast<SharedLibrary *>(&target))
-       {
-               Tool &copy = builder.get_toolchain().get_tool("CP");
-               InstalledFile *inst_tgt = dynamic_cast<InstalledFile *>(copy.create_target(target));
-               if(architecture->get_system()=="windows")
-                       builder.get_build_graph().add_installed_target(*shlib->get_import_library());
-               else
-               {
-                       string link_name = architecture->create_filename<SharedLibrary>(shlib->get_libname());
-                       if(link_name!=FS::basename(inst_tgt->get_path()))
-                               inst_tgt->set_symlink(link_name);
-               }
-               return inst_tgt;
-       }
-       else
-               return 0;
-}
-
-string GnuLinker::create_build_signature(const BuildInfo &binfo) const
-{
-       string result = Tool::create_build_signature(binfo);
-       result += ',';
-       if(binfo.libmode<=BuildInfo::STATIC)
-               result += 't';
-       else
-               result += 'd';
-       if(binfo.strip)
-               result += 's';
-       if(!binfo.libs.empty())
-       {
-               result += ",l";
-               result += join(binfo.libs.begin(), binfo.libs.end(), ",l");
-       }
-       return result;
-}
-
-void GnuLinker::do_prepare(ToolData &tool) const
-{
-       bool path_found = false;
-       const FS::Path &sysroot = tool.build_info.sysroot;
-       const std::string &tool_tag = static_cast<Tool &>(tool).get_tag();
-
-       const FileTarget *exe = static_cast<Tool &>(tool).get_executable();
-       if(exe)
-       {
-               ExternalTask::Arguments argv;
-               argv.push_back(exe->get_path().str());
-               argv.push_back("-v");
-               argv.push_back("-Wl,--verbose");
-               argv.push_back("-nostdlib");
-               if(!sysroot.empty())
-                       argv.push_back("--sysroot="+sysroot.str());
-
-               builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
-               try
-               {
-                       string output = ExternalTask::run_and_capture_output(argv, FS::Path(), true);
-
-                       string::size_type lib_path = output.find("LIBRARY_PATH=");
-                       if(lib_path!=string::npos)
-                       {
-                               string::size_type newline = output.find('\n', lib_path);
-                               for(const string &p: split(output.substr(lib_path+13, newline-lib_path-13), ':'))
-                               {
-                                       FS::Path path = strip(p);
-                                       if(!any_equals(tool.system_path, path))
-                                       {
-                                               builder.get_logger().log("tools", "Got %s frontend system path: %s", tool_tag, path);
-                                               tool.system_path.push_back(path);
-                                       }
-                                       path_found = true;
-                               }
-                       }
-
-                       string::size_type start = 0;
-                       while(start<output.size())
-                       {
-                               string::size_type search_dir = output.find("SEARCH_DIR(\"", start);
-                               if(search_dir==string::npos)
-                                       break;
-
-                               search_dir += 12;
-                               string::size_type end = output.find("\");", search_dir);
-                               if(end==string::npos)
-                                       break;
-
-                               FS::Path path;
-                               if(!output.compare(search_dir, 2, "=/"))
-                               {
-                                       search_dir += 2;
-                                       if(sysroot.empty())
-                                               path = "/";
-                                       else
-                                               path = sysroot;
-                               }
-
-                               path /= output.substr(search_dir, end-search_dir);
-                               if(!any_equals(tool.system_path, path))
-                               {
-                                       builder.get_logger().log("tools", "Got %s implicit system path: %s", tool_tag, path);
-                                       tool.system_path.push_back(path);
-                               }
-                               path_found = true;
-
-                               start = end+3;
-                       }
-               }
-               catch(...)
-               { }
-       }
-
-       if(!path_found)
-       {
-               builder.get_logger().log("tools", "No %s system path found, using defaults", tool_tag);
-               if(!sysroot.empty())
-                       tool.system_path.push_back(sysroot/"usr/lib");
-               else if(architecture->is_native())
-               {
-                       tool.system_path.push_back("/lib");
-                       tool.system_path.push_back("/usr/lib");
-                       if(architecture->match_name("pc-32-linux"))
-                       {
-                               tool.system_path.push_back("/lib/i386-linux-gnu");
-                               tool.system_path.push_back("/usr/lib/i386-linux-gnu");
-                       }
-                       else if(architecture->match_name("pc-64-linux"))
-                       {
-                               tool.system_path.push_back("/lib/x86_64-linux-gnu");
-                               tool.system_path.push_back("/usr/lib/x86_64-linux-gnu");
-                       }
-               }
-               else
-                       tool.system_path.push_back(format("/usr/%s/lib", architecture->get_cross_prefix()));
-       }
-}
-
-ExternalTask::Arguments GnuLinker::_run(const Binary &bin, FS::Path &work_dir)
-{
-       const Tool &tool = *bin.get_tool();
-       const Builder &builder = tool.get_builder();
-       const Architecture &arch = *tool.get_architecture();
-
-       ExternalTask::Arguments argv;
-       argv.push_back(tool.get_executable()->get_path().str());
-
-       if(const SharedLibrary *shlib = dynamic_cast<const SharedLibrary *>(&bin))
-       {
-               argv.push_back("-shared");
-               argv.push_back("-fPIC");
-               if(arch.get_system()!="windows" && !shlib->get_soname().empty())
-               {
-                       if(arch.get_system()=="darwin")
-                       {
-                               argv.push_back("-install_name");
-                               argv.push_back(shlib->get_soname());
-
-                               const string &ver = shlib->get_package()->get_version();
-                               const string &if_ver = shlib->get_package()->get_interface_version();
-                               if(!ver.empty() && !if_ver.empty())
-                               {
-                                       argv.push_back("-current_version");
-                                       argv.push_back(ver);
-                                       argv.push_back("-compatibility_version");
-                                       argv.push_back(if_ver);
-                               }
-                       }
-                       else
-                               argv.push_back("-Wl,-soname,"+shlib->get_soname());
-               }
-       }
-
-       BuildInfo binfo;
-       bin.collect_build_info(binfo);
-
-       const FS::Path &sysroot = binfo.sysroot;
-       if(!sysroot.empty())
-               argv.push_back("--sysroot="+sysroot.str());
-
-       FS::Path lib_dir = builder.get_prefix()/"lib";
-       if(binfo.rpath_mode==BuildInfo::ABSOLUTE)
-               argv.push_back("-Wl,-rpath,"+lib_dir.str());
-       else
-       {
-               if(binfo.rpath_mode==BuildInfo::RELATIVE)
-                       argv.push_back("-Wl,-rpath,$ORIGIN/../lib");
-               argv.push_back("-Wl,-rpath-link,"+lib_dir.str());
-       }
-
-       for(const FS::Path &p: binfo.libpath)
-               argv.push_back("-L"+p.str());
-       if(binfo.strip)
-               argv.push_back("-s");
-       if(binfo.threads && arch.get_system()!="windows" && arch.get_system()!="darwin")
-               argv.push_back("-pthread");
-
-       const Architecture &native_arch = builder.get_native_arch();
-       if(arch.is_native() && arch.get_bits()!=native_arch.get_bits())
-               argv.push_back(format("-m%d", arch.get_bits()));
-
-       argv.push_back("-o");
-       argv.push_back(relative(bin.get_path(), work_dir).str());
-
-       for(const string &s: binfo.keep_symbols)
-               argv.push_back("-u"+s);
-
-       bool static_link_ok = (binfo.libmode<=BuildInfo::STATIC);
-
-       bool has_cplusplus = false;
-       for(Target *d: bin.get_dependencies())
-       {
-               FileTarget *file = dynamic_cast<FileTarget *>(d);
-               Target *tgt = d->get_real_target();
-
-               if(ObjectFile *obj = dynamic_cast<ObjectFile *>(tgt))
-               {
-                       argv.push_back(relative(obj->get_path(), work_dir).str());
-                       if(obj->get_tool()->get_tag()=="CXX")
-                               has_cplusplus = true;
-               }
-               else if(StaticLibrary *stlib = dynamic_cast<StaticLibrary *>(tgt))
-                       argv.push_back((file?file:stlib)->get_path().str());
-               else if(SharedLibrary *shlib = dynamic_cast<SharedLibrary *>(tgt))
-               {
-                       argv.push_back("-l"+shlib->get_libname());
-                       static_link_ok = false;
-               }
-               else if(ImportLibrary *imp = dynamic_cast<ImportLibrary *>(tgt))
-               {
-                       shlib = imp->get_shared_library();
-                       if(shlib)
-                               argv.push_back("-l"+shlib->get_libname());
-                       else
-                               argv.push_back((file?file:imp)->get_path().str());
-                       static_link_ok = false;
-               }
-       }
-
-       for(const string &l: binfo.libs)
-               if(l.size()>10 && !l.compare(l.size()-10, 10, ".framework"))
-               {
-                       argv.push_back("-framework");
-                       argv.push_back(l.substr(0, l.size()-10));
-               }
-
-       if(static_link_ok)
-               argv.push_back("-static");
-       else
-       {
-               if(has_cplusplus)
-               {
-                       auto i = binfo.libmodes.find("stdc++");
-                       if(i!=binfo.libmodes.end() && i->second<=BuildInfo::STATIC)
-                               argv.push_back("-static-libstdc++");
-               }
-
-               if(arch.get_system()=="windows")
-                       argv.push_back("-Wl,--enable-auto-import");
-       }
-
-       return argv;
-}
diff --git a/source/gnulinker.h b/source/gnulinker.h
deleted file mode 100644 (file)
index a0632d6..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef GNULINKER_H_
-#define GNULINKER_H_
-
-#include "tool.h"
-
-class Binary;
-
-/**
-The GNU linker.  Turns ObjectFiles into Executables and SharedLibraries.  To
-create a shared library, specify "shared" as the second argument to
-create_target.
-
-Uses either gcc or g++ depending on what was used to compile the object files.
-*/
-class GnuLinker: public Tool
-{
-public:
-       GnuLinker(Builder &, const Architecture &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-       Target *create_install(Target &) const override;
-       std::string create_build_signature(const BuildInfo &) const override;
-protected:
-       void do_prepare(ToolData &) const override;
-private:
-       static ExternalTask::Arguments _run(const Binary &, Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/gnutools.cpp b/source/gnutools.cpp
deleted file mode 100644 (file)
index e64bcdb..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "builder.h"
-#include "gnuarchiver.h"
-#include "gnucompiler.h"
-#include "gnulinker.h"
-#include "gnutools.h"
-#include "mingwdlltool.h"
-
-GnuTools::GnuTools(Builder &builder, const Architecture &arch):
-       Toolchain("gnu", get_priority(arch))
-{
-       add_tool(new GnuCompiler(builder, arch, "CC"));
-       add_tool(new GnuCompiler(builder, arch, "CXX"));
-       add_tool(new GnuCompiler(builder, arch, "OBJC"));
-
-       add_tool(new GnuLinker(builder, arch));
-       add_tool(new GnuArchiver(builder, arch));
-
-       if(arch.get_system()=="windows")
-               add_tool(new MingwDllTool(builder, arch));
-}
-
-int GnuTools::get_priority(const Architecture &arch)
-{
-       if(arch.get_toolchain()=="gnu")
-               return 20;
-       else if(arch.get_system()=="linux")
-               return 10;
-       else
-               return 0;
-}
diff --git a/source/gnutools.h b/source/gnutools.h
deleted file mode 100644 (file)
index 5aeb966..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef GNUTOOLS_H_
-#define GNUTOOLS_H_
-
-#include "toolchain.h"
-
-class Architecture;
-class Builder;
-
-class GnuTools: public Toolchain
-{
-public:
-       GnuTools(Builder &, const Architecture &);
-
-       static int get_priority(const Architecture &);
-};
-
-#endif
diff --git a/source/importlibrary.cpp b/source/importlibrary.cpp
deleted file mode 100644 (file)
index 435fb30..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#include <msp/strings/format.h>
-#include "architecture.h"
-#include "builder.h"
-#include "component.h"
-#include "exportdefinitions.h"
-#include "importlibrary.h"
-#include "sharedlibrary.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-ImportLibrary::ImportLibrary(Builder &b, const Component &c, SharedLibrary &sl, ExportDefinitions &exp):
-       FileTarget(b, c.get_package(), c.get_package().get_output_directory()/generate_filename(c, sl)),
-       shared_lib(&sl)
-{
-       component = &c;
-       add_dependency(exp);
-       shared_lib->set_import_library(this);
-
-       install_location = "lib";
-
-       const string &version = component->get_package().get_interface_version();
-       if(!version.empty())
-       {
-               const Architecture &arch = builder.get_current_arch();
-               install_filename = arch.create_filename<ImportLibrary>(format("%s-%s", sl.get_libname(), version));
-       }
-}
-
-string ImportLibrary::generate_filename(const Component &comp, const SharedLibrary &sl)
-{
-       const Architecture &arch = comp.get_package().get_builder().get_current_arch();
-       return arch.create_filename<ImportLibrary>(sl.get_libname());
-}
diff --git a/source/importlibrary.h b/source/importlibrary.h
deleted file mode 100644 (file)
index 1c5d682..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef IMPORTLIBRARY_H_
-#define IMPORTLIBRARY_H_
-
-#include "filetarget.h"
-
-class ExportDefinitions;
-class SharedLibrary;
-
-/**
-A special case of static library which pulls in a shared library.  Used on
-platforms with no true dynamic linking support.
-*/
-class ImportLibrary: public FileTarget
-{
-private:
-       SharedLibrary *shared_lib = 0;
-
-public:
-       ImportLibrary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { }
-       ImportLibrary(Builder &, const Component &, SharedLibrary &, ExportDefinitions &);
-private:
-       static std::string generate_filename(const Component &, const SharedLibrary &);
-
-public:
-       const char *get_type() const override { return "ImportLibrary"; }
-
-       SharedLibrary *get_shared_library() const { return shared_lib; }
-};
-
-#endif
diff --git a/source/installcomponent.cpp b/source/installcomponent.cpp
deleted file mode 100644 (file)
index 86919fa..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "installcomponent.h"
-#include "builder.h"
-#include "file.h"
-#include "sourcepackage.h"
-#include "tool.h"
-
-using namespace std;
-using namespace Msp;
-
-void InstallComponent::create_targets() const
-{
-       Builder &builder = package.get_builder();
-       Target *inst = builder.get_build_graph().get_target("install");
-       Tool &copy = builder.get_toolchain().get_tool("CP");
-
-       for(const FS::Path &s: collect_source_files())
-       {
-               Target *tgt = builder.get_vfs().get_target(s);
-               if(!tgt)
-                       tgt = new File(builder, package, s);
-               inst->add_dependency(*copy.create_target(*tgt, name));
-       }
-}
diff --git a/source/installcomponent.h b/source/installcomponent.h
deleted file mode 100644 (file)
index 457256d..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef INSTALLCOMPONENT_H_
-#define INSTALLCOMPONENT_H_
-
-#include "component.h"
-
-class InstallComponent: public Component
-{
-public:
-       InstallComponent(SourcePackage &p, const std::string &n): Component(p, n) { }
-
-       void create_targets() const override;
-};
-
-#endif
diff --git a/source/installedfile.cpp b/source/installedfile.cpp
deleted file mode 100644 (file)
index 6e45ad2..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include "builder.h"
-#include "installedfile.h"
-#include "sharedlibrary.h"
-
-using namespace std;
-using namespace Msp;
-
-InstalledFile::InstalledFile(Builder &b, const SourcePackage &p, FileTarget &s, const string &loc):
-       FileTarget(b, p, generate_target_path(b.get_prefix(), s, loc)),
-       source(s)
-{
-       add_dependency(source);
-}
-
-FS::Path InstalledFile::generate_target_path(const FS::Path &global_prefix, const FileTarget &tgt, const string &loc)
-{
-       if(!tgt.is_installable() && loc.empty())
-               throw invalid_argument(tgt.get_name()+" is not installable");
-
-       FS::Path prefix;
-       FS::Path mid;
-       if(!loc.compare(0, 2, "//"))
-       {
-               if(!tgt.get_package())
-                       throw invalid_argument("No private install location for "+tgt.get_name());
-
-               prefix = tgt.get_package()->get_temp_directory();
-               mid = loc.substr(2);
-       }
-       else
-       {
-               prefix = global_prefix;
-
-               if(!loc.empty())
-                       mid = loc;
-               else if(const Component *comp = tgt.get_component())
-                       mid = comp->get_install_map().get_install_location(tgt);
-       }
-
-       if(mid.empty())
-               mid = tgt.get_install_location();
-
-       string fn = tgt.get_install_filename();
-       if(fn.empty())
-               fn = FS::basename(tgt.get_path());
-
-       return prefix/mid/fn;
-}
-
-void InstalledFile::set_symlink(const FS::Path &l)
-{
-       FS::Path al = FS::dirname(path)/l;
-       if(al==path)
-               throw invalid_argument("InstalledFile::set_symlink");
-       link = FS::dirname(path)/l;
-       builder.get_vfs().register_path(link, this);
-}
-
-Target *InstalledFile::get_real_target()
-{
-       return source.get_real_target();
-}
-
-void InstalledFile::check_rebuild()
-{
-       if(!mtime)
-               mark_rebuild("Does not exist");
-       else if(source.get_mtime()>mtime || source.get_size()!=size)
-               mark_rebuild(source.get_name()+" has changed");
-       else if(source.needs_rebuild())
-               mark_rebuild(source.get_name()+" needs rebuilding");
-       if(!needs_rebuild() && !link.empty())
-       {
-               if(!FS::exists(link))
-                       mark_rebuild("Symlink does not exist");
-               else
-               {
-                       FS::Path rel_path = FS::relative(path, FS::dirname(link));
-                       if(FS::readlink(link)!=rel_path)
-                               mark_rebuild("Symlink needs updating");
-               }
-       }
-}
-
-void InstalledFile::clean()
-{
-       if(!link.empty() && mtime)
-               FS::unlink(link);
-       FileTarget::clean();
-}
diff --git a/source/installedfile.h b/source/installedfile.h
deleted file mode 100644 (file)
index 0dd671f..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef INSTALLEDFILE_H_
-#define INSTALLEDFILE_H_
-
-#include "sourcepackage.h"
-#include "filetarget.h"
-
-/**
-Represents the installation of a file.
-*/
-class InstalledFile: public FileTarget
-{
-private:
-       FileTarget &source;
-       Msp::FS::Path link;
-
-public:
-       InstalledFile(Builder &, const SourcePackage &, FileTarget &, const std::string & =std::string());
-private:
-       static Msp::FS::Path generate_target_path(const Msp::FS::Path &, const FileTarget &i, const std::string &);
-
-public:
-       const char *get_type() const override { return "InstalledFile"; }
-       FileTarget &get_source() const { return source; }
-
-       /** Sets a symlink for the file.  A relative path will be rooted at the
-       directory the file resides in. */
-       void set_symlink(const Msp::FS::Path &);
-
-       const Msp::FS::Path &get_symlink() const { return link; }
-
-       Target *get_real_target() override;
-private:
-       void check_rebuild() override;
-
-public:
-       void clean() override;
-};
-
-#endif
diff --git a/source/installmap.cpp b/source/installmap.cpp
deleted file mode 100644 (file)
index 448f997..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/fs/utils.h>
-#include "component.h"
-#include "filetarget.h"
-#include "installmap.h"
-#include "sourcepackage.h"
-#include "templatefile.h"
-
-using namespace std;
-using namespace Msp;
-
-void InstallMap::add_mapping(const FS::Path &src, const FS::Path &inst)
-{
-       Entry e;
-       e.source = src;
-       e.install = inst;
-       entries.push_back(e);
-}
-
-FS::Path InstallMap::get_install_location(const FileTarget &target) const
-{
-       const Component *comp = target.get_component();
-       unsigned overlay_depth = 0;
-       if(comp && !comp->get_overlays().empty())
-       {
-               // Check if the target resides in an overlay directory
-               string last_dir = FS::basename(FS::dirname(target.get_path()));
-               if(any_equals(comp->get_overlays(), last_dir))
-                       overlay_depth = 1;
-       }
-
-       FS::Path source = target.get_path();
-       if(comp)
-       {
-               /* Check if the target is a generated source file, residing in the
-               temporary directory */
-               const SourcePackage &pkg = comp->get_package();
-               int temp_depth = FS::descendant_depth(source, pkg.get_temp_directory());
-               if(temp_depth>0)
-               {
-                       // If it is, use the generating template's directory instead
-                       for(Target *d: target.get_dependencies())
-                               if(const TemplateFile *tmpl = dynamic_cast<const TemplateFile *>(d))
-                               {
-                                       source = FS::dirname(tmpl->get_path())/FS::basename(source);
-                                       break;
-                               }
-               }
-       }
-
-       /* Look for a mapping entry matching both the target's original location
-       and default install location */
-       FS::Path install = target.get_install_location();
-       for(const Entry &e: entries)
-       {
-               int source_depth = FS::descendant_depth(source, e.source);
-               if(source_depth>=0)
-               {
-                       FS::Path install_base = FS::common_ancestor(install, e.install);
-                       if(install_base.size()>1)
-                       {
-                               install = e.install/source.subpath(e.source.size(), source_depth-1-overlay_depth);
-                               break;
-                       }
-               }
-       }
-
-       return install;
-}
-
-
-InstallMap::Loader::Loader(InstallMap &m, const FS::Path &s):
-       DataFile::ObjectLoader<InstallMap>(m),
-       source_base(s)
-{
-       add("map", &Loader::map);
-}
-
-void InstallMap::Loader::map(const string &src, const string &inst)
-{
-       obj.add_mapping(source_base/src, inst);
-}
diff --git a/source/installmap.h b/source/installmap.h
deleted file mode 100644 (file)
index 49211d7..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef INSTALLMAP_H_
-#define INSTALLMAP_H_
-
-#include <vector>
-#include <msp/datafile/objectloader.h>
-#include <msp/fs/path.h>
-
-class FileTarget;
-
-/**
-Maps install locations based on location in the source tree.  Mappings are
-defined as pairs of source and install locations.  Targets within a source
-location are mapped if their default install location shares a common prefix
-with the mapped install location.  The remainder of the source location is
-appended to the mapped install location to form the final install location.
-*/
-class InstallMap
-{
-public:
-       class Loader: public Msp::DataFile::ObjectLoader<InstallMap>
-       {
-       private:
-               Msp::FS::Path source_base;
-
-       public:
-               Loader(InstallMap &, const Msp::FS::Path &);
-
-       private:
-               void map(const std::string &, const std::string &);
-       };
-
-private:
-       struct Entry
-       {
-               Msp::FS::Path source;
-               Msp::FS::Path install;
-       };
-
-       std::vector<Entry> entries;
-
-public:
-       /** Adds an install mapping.  Multiple mappings can be specified for the
-       same source location, but the first one that matches both that and the
-       target's default install location will be used. */
-       void add_mapping(const Msp::FS::Path &, const Msp::FS::Path &);
-
-       /** Returns the install location for a target.  If no defined mappings match
-       the target, its default install location is returned. */
-       Msp::FS::Path get_install_location(const FileTarget &) const;
-};
-
-#endif
diff --git a/source/internaltask.cpp b/source/internaltask.cpp
deleted file mode 100644 (file)
index fc929ce..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "internaltask.h"
-
-InternalTask::~InternalTask()
-{
-       worker.join();
-}
-
-void InternalTask::start()
-{
-       prepare();
-       worker.launch();
-}
-
-Task::Status InternalTask::check()
-{
-       Status result = worker.get_status();
-       if(result!=RUNNING)
-               signal_finished.emit(result==SUCCESS);
-       return result;
-}
-
-Task::Status InternalTask::wait()
-{
-       Status result;
-       while((result = check())==RUNNING) ;
-       return result;
-}
-
-
-void InternalTask::Worker::main()
-{
-       if(func())
-               status = Task::SUCCESS;
-       else
-               status = Task::ERROR;
-}
diff --git a/source/internaltask.h b/source/internaltask.h
deleted file mode 100644 (file)
index 608ea96..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef INTERNALTASK_H_
-#define INTERNALTASK_H_
-
-#include <functional>
-#include <msp/core/thread.h>
-#include "task.h"
-
-/**
-Runs a worker thread.  Tools should derive a thread class from
-InternalTask::Worker.  The worker thread must set its status to either SUCCESS
-or ERROR before terminating.
-*/
-class InternalTask: public Task
-{
-private:
-       class Worker: public Msp::Thread
-       {
-               friend class InternalTask;
-
-       private:
-               std::function<bool()> func;
-               volatile Status status = Task::RUNNING;
-
-       public:
-               Worker(std::function<bool()> f): func(f) { }
-
-               Status get_status() const { return status; }
-
-       private:
-               void main() override;
-       };
-
-       Worker worker;
-
-public:
-       InternalTask(std::function<bool()> f): worker(f) { }
-       ~InternalTask();
-
-       std::string get_command() const override { return "<internal>"; }
-       void start() override;
-       Status check() override;
-       Status wait() override;
-};
-
-#endif
diff --git a/source/jarsigner.cpp b/source/jarsigner.cpp
deleted file mode 100644 (file)
index a2a1c73..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#include <msp/core/environ.h>
-#include <msp/fs/utils.h>
-#include "component.h"
-#include "filetarget.h"
-#include "jarsigner.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-JarSigner::JarSigner(Builder &b):
-       Tool(b, "JSGN")
-{
-       set_command("jarsigner");
-       set_run_external(_run);
-}
-
-Target *JarSigner::create_target(const vector<Target *> &, const string &)
-{
-       throw logic_error("not implemented");
-}
-
-ExternalTask::Arguments JarSigner::_run(const FileTarget &file, FS::Path &work_dir)
-{
-       const Tool &tool = *file.get_tool();
-
-       ExternalTask::Arguments argv;
-       argv.push_back(tool.get_executable()->get_path().str());
-
-       // TODO Make this generic
-       FS::Path home_dir = Msp::getenv("HOME");
-       argv.push_back("-keystore");
-       argv.push_back((home_dir/".android"/"debug.keystore").str());
-       argv.push_back("-storepass");
-       argv.push_back("android");
-
-       argv.push_back(FS::relative(file.get_path(), work_dir).str());
-       argv.push_back("androiddebugkey");
-
-       return argv;
-}
diff --git a/source/jarsigner.h b/source/jarsigner.h
deleted file mode 100644 (file)
index d836348..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef JARSIGNER_H_
-#define JARSIGNER_H_
-
-#include "tool.h"
-
-class FileTarget;
-
-class JarSigner: public Tool
-{
-public:
-       JarSigner(Builder &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static ExternalTask::Arguments _run(const FileTarget &, Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/lib/architecture.cpp b/source/lib/architecture.cpp
new file mode 100644 (file)
index 0000000..54ffcf0
--- /dev/null
@@ -0,0 +1,337 @@
+#include <limits>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include "architecture.h"
+#include "builder.h"
+#include "executable.h"
+#include "importlibrary.h"
+#include "objectfile.h"
+#include "sharedlibrary.h"
+#include "staticlibrary.h"
+#include "sysutils.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace {
+
+const char *types[] =
+{
+       "x86",
+       "arm",
+       "ppc",
+       0
+};
+
+const char *cpus[] =
+{
+       "i386",       "x86",
+       "i486",       "x86",
+       "pentium",    "x86",
+       "pentiumpro", "x86",
+       "pentium2",   "x86",
+       "pentium3",   "x86",
+       "pentium4",   "x86",
+       "core2",      "x86",
+       "nehalem",    "x86",
+       "k6",         "x86",
+       "athlon",     "x86",
+       "athlonxp",   "x86",
+       "athlon64",   "x86",
+       "armv5",      "arm",
+       "armv6",      "arm",
+       "armv7",      "arm",
+       "armv7a",     "arm",
+       0
+};
+
+const char *fpus[] =
+{
+       "387",   "x86",
+       "sse",   "x86",
+       "sse3",  "x86",
+       "sse4.1", "x86",
+       "vfpv3", "arm",
+       "neon",  "arm",
+       0
+};
+
+const char *systems[] =
+{
+       "linux",
+       "freebsd",
+       "darwin",
+       "windows",
+       "android",
+       0
+};
+
+const char *toolchains[] =
+{
+       "gnu",
+       "clang",
+       "msvc",
+       0
+};
+
+const char *aliases[] =
+{
+       "pc",              "x86",
+       "x86_64",          "x86-64",
+       "x64",             "x86-64",
+       "amd64",           "x86-64",
+       "i586",            "pentium",
+       "i686",            "pentiumpro",
+       "corei7",          "nehalem",
+       "win32",           "windows-32",
+       "win64",           "windows-64",
+       "power macintosh", "ppc",
+       "armeabi",         "arm",
+       "v7a",             "armv7a",
+       "gcc",             "gnu",
+       "mingw",           "windows-gnu",
+       0
+};
+
+}
+
+Architecture::Architecture(Builder &b, const string &spec):
+       builder(b)
+{
+       if(spec.empty())
+       {
+               parse_specification(get_system_type());
+               // We really only want to set type for the default arch
+               cpu.clear();
+               bits = sizeof(void *)*numeric_limits<unsigned char>::digits;
+               native = true;
+       }
+       else
+       {
+               parse_specification(spec);
+               const Architecture &native_arch = builder.get_native_arch();
+               if(type.empty())
+                       type = native_arch.type;
+               if(system.empty())
+                       system = native_arch.system;
+               if(!bits)
+               {
+                       if(type==native_arch.type)
+                               bits = native_arch.bits;
+                       else
+                               bits = 32;
+               }
+
+               if(type!=native_arch.type || system!=native_arch.system)
+                       cross_prefix = format("%s-%s", type, system);
+               else if(bits==native_arch.bits)
+                       native = true;
+       }
+
+       update();
+}
+
+void Architecture::refine(const string &spec)
+{
+       parse_specification(spec);
+       update();
+}
+
+void Architecture::update()
+{
+       name = type;
+       if(!cpu.empty())
+               name += format("-%s", cpu);
+       if(!fpu.empty())
+               name += format("-%s", fpu);
+       name += format("-%d-%s", bits, system);
+       if(!toolchain.empty())
+               name += format("-%s", toolchain);
+
+       filename_patterns.clear();
+       if(system=="windows")
+       {
+               add_pattern<SharedLibrary>("%.dll");
+               if(toolchain=="msvc")
+               {
+                       add_pattern<ObjectFile>("%.obj");
+                       add_pattern<ImportLibrary>("%.lib");
+                       add_pattern<StaticLibrary>("%_static.lib");
+               }
+               else
+               {
+                       add_pattern<ObjectFile>("%.o");
+                       add_pattern<SharedLibrary>("lib%.dll");
+                       add_pattern<ImportLibrary>("lib%.dll.a");
+                       add_pattern<StaticLibrary>("lib%.a");
+               }
+               add_pattern<Executable>("%.exe");
+       }
+       else
+       {
+               add_pattern<ObjectFile>("%.o");
+               if(system=="darwin")
+                       add_pattern<SharedLibrary>("lib%.dylib");
+               else
+                       add_pattern<SharedLibrary>("lib%.so");
+               add_pattern<StaticLibrary>("lib%.a");
+               add_pattern<Executable>("%");
+       }
+}
+
+bool Architecture::match_name(const string &pattern, unsigned *quality) const
+{
+       bool negate = (pattern[0]=='!');
+       vector<string> parts = split(pattern.substr(negate), "-");
+       resolve_aliases(parts);
+       for(const string &p: parts)
+       {
+               if((p=="32" && bits==32) || (p=="64" && bits==64))
+                       ;
+               else if(p!=type && p!=cpu && p!=fpu && p!=system && p!=toolchain)
+               {
+                       if(quality)
+                               *quality = 0;
+                       return negate;
+               }
+       }
+
+       if(quality)
+               *quality = parts.size();
+       return !negate;
+}
+
+string Architecture::best_match(const vector<string> &names) const
+{
+       string best;
+       unsigned best_quality = 0;
+       for(const string &n: names)
+       {
+               unsigned quality;
+               if(match_name(n, &quality))
+                       if(quality>best_quality)
+                       {
+                               best = n;
+                               best_quality = quality;
+                       }
+       }
+
+       return best;
+}
+
+template<typename T>
+void Architecture::add_pattern(const string &pat)
+{
+       filename_patterns[typeid(T).name()].push_back(Pattern(pat));
+}
+
+void Architecture::resolve_aliases(vector<string> &parts)
+{
+       for(unsigned i=0; i<parts.size(); ++i)
+       {
+               const string &part = parts[i];
+               const char *replace = 0;
+               for(unsigned j=0; (!replace && aliases[j]); j+=2)
+                       if(part==aliases[j])
+                               replace = aliases[j+1];
+
+               if(replace)
+               {
+                       bool has_dash = false;
+                       for(const char *c=replace; (!has_dash && *c); ++c)
+                               has_dash = (*c=='-');
+
+                       if(has_dash)
+                       {
+                               vector<string> rparts = split(replace, "-");
+                               parts[i] = rparts[0];
+                               parts.insert(parts.begin()+i+1, rparts.begin()+1, rparts.end());
+                               i += rparts.size()-1;
+                       }
+                       else
+                               parts[i] = replace;
+               }
+       }
+}
+
+void Architecture::parse_specification(const string &spec)
+{
+       vector<string> parts = split(spec, "-");
+       resolve_aliases(parts);
+       for(const string &p: parts)
+       {
+               bool ok = false;
+
+               for(unsigned j=0; (!ok && types[j]); ++j)
+                       if(p==types[j])
+                       {
+                               if(!type.empty() && p!=type)
+                                       throw invalid_argument("Conflicting type specification");
+                               type = p;
+                               ok = true;
+                       }
+
+               for(unsigned j=0; (!ok && cpus[j]); j+=2)
+                       if(p==cpus[j])
+                       {
+                               if(type.empty())
+                                       type = cpus[j+1];
+                               else if(cpus[j+1]!=type)
+                                       throw invalid_argument("Conflicting CPU specification");
+                               cpu = p;
+                               ok = true;
+                       }
+
+               for(unsigned j=0; (!ok && fpus[j]); j+=2)
+                       if(p==fpus[j])
+                       {
+                               if(fpus[j+1]!=type)
+                                       throw invalid_argument("Conflicting FPU specification");
+                               fpu = p;
+                               ok = true;
+                       }
+
+               for(unsigned j=0; (!ok && systems[j]); ++j)
+                       if(p==systems[j])
+                       {
+                               if(!system.empty() && p!=system)
+                                       throw invalid_argument("Conflicting system specification");
+                               system = p;
+                               ok = true;
+                       }
+
+               for(unsigned j=0; (!ok && toolchains[j]); ++j)
+                       if(p==toolchains[j])
+                       {
+                               if(!toolchain.empty() && p!=toolchain)
+                                       throw invalid_argument("Conflicting toolchain specification");
+                               toolchain = p;
+                               ok = true;
+                       }
+
+               if(!ok && (p=="32" || p=="64"))
+               {
+                       unsigned b = lexical_cast<unsigned>(p);
+                       if(bits && b!=bits)
+                               throw invalid_argument("Conflicting bits specification");
+                       bits = b;
+                       ok = true;
+               }
+
+               if(!ok)
+                       throw invalid_argument("Unrecognized part in arch specification: "+p);
+       }
+}
+
+
+Architecture::Loader::Loader(Architecture &a):
+       DataFile::ObjectLoader<Architecture>(a)
+{
+       add("prefix", &Loader::cross_prefix);
+}
+
+void Architecture::Loader::cross_prefix(const string &p)
+{
+       if(!obj.native)
+               obj.cross_prefix = p;
+}
diff --git a/source/lib/architecture.h b/source/lib/architecture.h
new file mode 100644 (file)
index 0000000..2cf4fa2
--- /dev/null
@@ -0,0 +1,95 @@
+#ifndef ARCHITECTURE_H_
+#define ARCHITECTURE_H_
+
+#include <typeinfo>
+#include <msp/datafile/loader.h>
+#include "buildinfo.h"
+#include "pattern.h"
+
+class Builder;
+
+/**
+Stores information about an architecture.  This includes CPU type, model and
+bitness and operating system.
+*/
+class Architecture
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<Architecture>
+       {
+       public:
+               Loader(Architecture &);
+
+       private:
+               void cross_prefix(const std::string &);
+       };
+
+private:
+       Builder &builder;
+       std::string type;
+       std::string cpu;
+       std::string fpu;
+       std::string system;
+       unsigned bits = 0;
+       std::string toolchain;
+       std::string name;
+       bool native = false;
+       std::string cross_prefix;
+       std::map<std::string, std::vector<Pattern>> filename_patterns;
+
+public:
+       Architecture(Builder &b, const std::string &spec);
+
+       void refine(const std::string &);
+private:
+       void update();
+
+public:
+       const std::string &get_type() const { return type; }
+       const std::string &get_name() const { return name; }
+       const std::string &get_system() const { return system; }
+       unsigned get_bits() const { return bits; }
+       const std::string &get_cpu() const { return cpu; }
+       const std::string &get_fpu() const { return fpu; }
+       const std::string &get_toolchain() const { return toolchain; }
+       bool match_name(const std::string &, unsigned * = 0) const;
+       std::string best_match(const std::vector<std::string> &) const;
+       bool is_native() const { return native; }
+       bool is_cross() const { return !cross_prefix.empty(); }
+
+       const std::string &get_cross_prefix() const { return cross_prefix; }
+
+       template<typename T>
+       const std::vector<Pattern> &get_patterns() const;
+
+       template<typename T>
+       std::string create_filename(const std::string &) const;
+
+private:
+       template<typename T>
+       void add_pattern(const std::string &);
+
+private:
+       static void resolve_aliases(std::vector<std::string> &);
+       void parse_specification(const std::string &);
+};
+
+template<typename T>
+inline const std::vector<Pattern> &Architecture::get_patterns() const
+{
+       auto i = filename_patterns.find(typeid(T).name());
+       if(i!=filename_patterns.end())
+               return i->second;
+
+       static std::vector<Pattern> empty;
+       return empty;
+}
+
+template<typename T>
+inline std::string Architecture::create_filename(const std::string &base) const
+{
+       const std::vector<Pattern> &patterns = get_patterns<T>();
+       return patterns.empty() ? base : patterns.front().apply(base);
+}
+
+#endif
diff --git a/source/lib/binary.cpp b/source/lib/binary.cpp
new file mode 100644 (file)
index 0000000..f245037
--- /dev/null
@@ -0,0 +1,96 @@
+#include <msp/core/algorithm.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include "binary.h"
+#include "builder.h"
+#include "component.h"
+#include "objectfile.h"
+#include "sharedlibrary.h"
+#include "sourcepackage.h"
+#include "staticlibrary.h"
+#include "tool.h"
+
+using namespace std;
+using namespace Msp;
+
+Binary::Binary(Builder &b, const Component &c, const string &p, const vector<ObjectFile *> &objs):
+       FileTarget(b, c.get_package(), c.get_package().get_output_directory()/p),
+       objects(objs)
+{
+       component = &c;
+       for(ObjectFile *o: objects)
+               add_dependency(*o);
+
+       nested_build_sig = true;
+       arch_in_build_sig = true;
+}
+
+void Binary::collect_build_info(BuildInfo &binfo) const
+{
+       for(ObjectFile *o: objects)
+               if(const Tool *obj_tool = o->get_tool())
+                       binfo.update_from(obj_tool->get_build_info());
+
+       Target::collect_build_info(binfo);
+
+       binfo.update_from(static_binfo);
+}
+
+void Binary::find_dependencies()
+{
+       if(!component)
+               return;
+
+       vector<Target *> static_libs;
+       vector<Target *> shared_libs;
+       vector<string> missing_libs;
+       find_dependencies(this, static_libs, shared_libs, missing_libs);
+
+       for(Target *t: static_libs)
+               add_dependency(*t);
+       for(Target *t: shared_libs)
+               add_dependency(*t);
+       for(const string &m: missing_libs)
+               problems.push_back(format("Required library %s not found", m));
+}
+
+void Binary::find_dependencies(Target *tgt, vector<Target *> &static_libs, vector<Target *> &shared_libs, vector<string> &missing_libs)
+{
+       BuildInfo binfo;
+       tgt->collect_build_info(binfo);
+       if(tgt!=this)
+       {
+               static_binfo.libpath.insert(static_binfo.libpath.end(), binfo.libpath.begin(), binfo.libpath.end());
+               static_binfo.keep_symbols.insert(static_binfo.keep_symbols.end(), binfo.keep_symbols.begin(), binfo.keep_symbols.end());
+               if(binfo.threads)
+                       static_binfo.threads = true;
+       }
+
+       for(const string &l: binfo.libs)
+       {
+               if(l.size()>10 && !l.compare(l.size()-10, 10, ".framework"))
+                       continue;
+
+               BuildInfo::LibraryMode libmode = component->get_build_info().get_libmode_for(l);
+               Target *lib = builder.get_vfs().find_library(l, binfo.libpath, libmode);
+               if(lib)
+               {
+                       Target *real = lib->get_real_target();
+                       if(StaticLibrary *stlib = dynamic_cast<StaticLibrary *>(real))
+                       {
+                               /* Keep only the last occurrence of each static library.  This
+                               ensures the order is correct for linking. */
+                               auto i = find(static_libs, stlib);
+                               if(i!=static_libs.end())
+                                       static_libs.erase(i);
+                               static_libs.push_back(stlib);
+                               find_dependencies(stlib, static_libs, shared_libs, missing_libs);
+                       }
+                       else if(!any_equals(shared_libs, lib))
+                               shared_libs.push_back(lib);
+               }
+               else if(!any_equals(missing_libs, l))
+                       missing_libs.push_back(l);
+       }
+}
diff --git a/source/lib/binary.h b/source/lib/binary.h
new file mode 100644 (file)
index 0000000..ac5a855
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef BINARY_H_
+#define BINARY_H_
+
+#include "buildinfo.h"
+#include "filetarget.h"
+
+class Component;
+class ObjectFile;
+
+/**
+Produces a binary file, which may be either a standalone executable or a shared
+library.
+*/
+class Binary: public FileTarget
+{
+private:
+       BuildInfo static_binfo;
+
+protected:
+       std::vector<ObjectFile *> objects;
+
+       Binary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { }
+       Binary(Builder &, const Component &, const std::string &, const std::vector<ObjectFile *> &);
+
+public:
+       void collect_build_info(BuildInfo &) const override;
+
+protected:
+       void find_dependencies() override;
+private:
+       void find_dependencies(Target *, std::vector<Target *> &, std::vector<Target *> &, std::vector<std::string> &);
+};
+
+#endif
diff --git a/source/lib/binarycomponent.cpp b/source/lib/binarycomponent.cpp
new file mode 100644 (file)
index 0000000..3209ccc
--- /dev/null
@@ -0,0 +1,149 @@
+#include <msp/fs/utils.h>
+#include "binarycomponent.h"
+#include "builder.h"
+#include "filetarget.h"
+#include "sourcepackage.h"
+#include "tool.h"
+
+using namespace std;
+using namespace Msp;
+
+void BinaryComponent::create_build_info()
+{
+       Component::create_build_info();
+
+       for(const Component *u: uses)
+       {
+               /* Select an include path that contains all the sources for this and the
+               used component.  This should produce a sensible result in most cases. */
+               FS::Path base;
+               for(const FS::Path &s: sources)
+                       base = base.empty() ? s : FS::common_ancestor(base, s);
+               for(const FS::Path &s: u->get_sources())
+                       base = FS::common_ancestor(base, s);
+               build_info.incpath.push_back(base);
+               build_info.libs.push_back(u->get_name());
+               if(!u->get_install())
+               {
+                       build_info.libmodes[u->get_name()] = BuildInfo::STATIC;
+                       build_info.libpath.push_back(u->get_package().get_output_directory());
+               }
+       }
+
+       if(type==LIBRARY || type==MODULE)
+               if(build_info.libmode<BuildInfo::DYNAMIC)
+                       build_info.libmode = BuildInfo::DYNAMIC;
+}
+
+void BinaryComponent::update_exported_build_info(BuildInfo &binfo) const
+{
+       if(type==LIBRARY)
+               binfo.libs.push_back(name);
+}
+
+void BinaryComponent::create_targets() const
+{
+       Builder &builder = package.get_builder();
+       BuildGraph &build_graph = builder.get_build_graph();
+       const Toolchain &toolchain = builder.get_toolchain();
+       const Toolchain &pkg_tools = package.get_toolchain();
+
+       vector<Target *> objs;
+       vector<FS::Path> source_filenames = collect_source_files();
+       objs.reserve(source_filenames.size());
+       for(auto i=source_filenames.begin(); i!=source_filenames.end(); ++i)
+       {
+               string ext = FS::extpart(FS::basename(*i));
+               Target *src = 0;
+
+               Tool *gen = pkg_tools.get_tool_for_suffix(ext);
+               if(gen)
+               {
+                       vector<Target *> templates;
+                       templates.push_back(gen->create_source(*this, *i));
+
+                       Tool::ProcessingUnit processing_unit = gen->get_processing_unit();
+                       if(processing_unit!=Tool::ONE_FILE)
+                       {
+                               FS::Path source_dir = FS::dirname(*i);
+                               for(auto j=next(i); j!=source_filenames.end(); )
+                               {
+                                       if((processing_unit!=Tool::DIRECTORY || FS::dirname(*j)==source_dir) &&
+                                               pkg_tools.get_tool_for_suffix(FS::extpart(FS::basename(*j)))==gen)
+                                       {
+                                               templates.push_back(gen->create_source(*this, *j));
+                                               // Remove additional files so they won't get processed again
+                                               j = source_filenames.erase(j);
+                                       }
+                                       else
+                                               ++j;
+                               }
+                       }
+
+                       src = gen->create_target(templates);
+                       ext = FS::extpart(FS::basename(dynamic_cast<FileTarget &>(*src).get_path()));
+               }
+
+               Tool *tool = toolchain.get_tool_for_suffix(ext, true);
+               if(tool)
+               {
+                       if(!src)
+                               src = tool->create_source(*this, *i);
+                       if(!src)
+                               continue;
+
+                       if(tool->accepts_suffix(ext))
+                       {
+                               Target *obj = tool->create_target(*src);
+                               objs.push_back(obj);
+                       }
+
+                       if(type==LIBRARY && install)
+                       {
+                               if(dynamic_cast<FileTarget *>(src)->is_installable())
+                                       build_graph.add_installed_target(*src);
+
+                               for(Target *s: src->get_side_effects())
+                                       if(dynamic_cast<FileTarget *>(s)->is_installable())
+                                               build_graph.add_installed_target(*s);
+                       }
+               }
+       }
+
+       Tool &linker = toolchain.get_tool("LINK");
+
+       vector<Target *> results;
+       results.reserve(2);
+       if(type==LIBRARY)
+       {
+               Tool &archiver = toolchain.get_tool("AR");
+               results.push_back(linker.create_target(objs, "shared"));
+               results.push_back(archiver.create_target(objs));
+       }
+       else if(type==MODULE)
+               results.push_back(linker.create_target(objs, "shared"));
+       else
+               results.push_back(linker.create_target(objs));
+
+       for(Target *r: results)
+       {
+               build_graph.add_primary_target(*r);
+               if(install)
+                       build_graph.add_installed_target(*r);
+       }
+}
+
+BinaryComponent::Loader::Loader(BinaryComponent &c):
+       DataFile::DerivedObjectLoader<BinaryComponent, Component::Loader>(c)
+{
+       add("use", &Loader::use);
+}
+
+void BinaryComponent::Loader::use(const string &n)
+{
+       const BinaryComponent *comp = dynamic_cast<const BinaryComponent *>(&obj.package.get_component(n));
+       if(!comp || comp->type!=LIBRARY)
+               throw logic_error(n+" is not a library");
+
+       obj.uses.push_back(comp);
+}
diff --git a/source/lib/binarycomponent.h b/source/lib/binarycomponent.h
new file mode 100644 (file)
index 0000000..ef145da
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef BINARYCOMPONENT_H_
+#define BINARYCOMPONENT_H_
+
+#include "component.h"
+
+class BinaryComponent: public Component
+{
+public:
+       class Loader: public Msp::DataFile::DerivedObjectLoader<BinaryComponent, Component::Loader>
+       {
+       public:
+               Loader(BinaryComponent &);
+       private:
+               void use(const std::string &);
+       };
+
+       enum Type
+       {
+               LIBRARY,
+               PROGRAM,
+               MODULE
+       };
+
+private:
+       Type type;
+       std::vector<const Component *> uses;
+
+public:
+       BinaryComponent(SourcePackage &p, const std::string &n, Type t): Component(p, n), type(t) { }
+
+       Type get_type() const { return type; }
+
+       void create_build_info() override;
+       void update_exported_build_info(BuildInfo &) const override;
+       void create_targets() const override;
+};
+
+#endif
diff --git a/source/lib/binarypackage.cpp b/source/lib/binarypackage.cpp
new file mode 100644 (file)
index 0000000..adfca1a
--- /dev/null
@@ -0,0 +1,169 @@
+#include <msp/core/algorithm.h>
+#include <msp/io/print.h>
+#include <msp/strings/utils.h>
+#include "binarypackage.h"
+#include "builder.h"
+#include "filetarget.h"
+#include "staticlibrary.h"
+
+using namespace std;
+using namespace Msp;
+
+BinaryPackage::BinaryPackage(Builder &b, const string &n):
+       Package(b, n)
+{
+       use_pkgconfig = false;
+}
+
+BinaryPackage *BinaryPackage::from_flags(Builder &builder, const string &name, const Flags &flags, const Flags &static_flags)
+{
+       BinaryPackage *pkg = new BinaryPackage(builder, name);
+       pkg->use_pkgconfig = true;
+
+       process_flags(flags, pkg->export_binfo);
+
+       Flags exclusive_static_flags;
+       for(const string &f: static_flags)
+               if(!any_equals(flags, f))
+                       exclusive_static_flags.push_back(f);
+       process_flags(exclusive_static_flags, pkg->static_binfo);
+
+       return pkg;
+}
+
+void BinaryPackage::process_flags(const Flags &flags, BuildInfo &binfo)
+{
+       for(const string &f: flags)
+       {
+               if(!f.compare(0, 2, "-I"))
+                       binfo.incpath.push_back(f.substr(2));
+               else if(!f.compare(0, 2, "-D"))
+               {
+                       string::size_type equals = f.find('=');
+                       if(equals!=string::npos)
+                               binfo.defines[f.substr(2, equals-2)] = f.substr(equals+1);
+                       else
+                               binfo.defines[f.substr(2)] = string();
+               }
+               else if(!f.compare(0, 2, "-L"))
+                       binfo.libpath.push_back(f.substr(2));
+               else if(!f.compare(0, 2, "-l"))
+                       binfo.libs.push_back(f.substr(2));
+               else if(f=="-pthread")
+                       binfo.threads = true;
+       }
+}
+
+void BinaryPackage::do_prepare()
+{
+       auto is_relative = [](const FS::Path &p){ return !p.is_absolute(); };
+       bool has_relative_paths = any_of(export_binfo.libpath.begin(), export_binfo.libpath.end(), is_relative) ||
+               any_of(export_binfo.incpath.begin(), export_binfo.incpath.end(), is_relative);
+
+       vector<FS::Path> bases;
+
+       /* If we have any relative paths that need resolving, or we have no paths at
+       all and are not using pkg-config, look for files in prefix */
+       if(has_relative_paths || (!use_pkgconfig && export_binfo.libpath.empty() && export_binfo.incpath.empty()))
+               bases.push_back(builder.get_prefix());
+
+       // Always look in system locations
+       bases.push_back(FS::Path());
+
+       bool system = false;
+       for(const FS::Path &b: bases)
+       {
+               FS::Path prefix = b;
+               system = prefix.empty();
+               if(system)
+               {
+                       prefix = "/usr";
+                       const Architecture &arch = builder.get_current_arch();
+                       if(arch.is_cross())
+                               prefix /= arch.get_cross_prefix();
+               }
+
+               VirtualFileSystem::SearchPath libpath = export_binfo.libpath;
+               if(!system && libpath.empty())
+                       libpath.push_back("lib");
+               for(FS::Path &p: libpath)
+                       p = prefix/p;
+
+               bool all_found = true;
+               for(const string &l: export_binfo.libs)
+                       all_found &= (builder.get_vfs().find_library(l, libpath, export_binfo.libmode, system)!=0);
+
+               VirtualFileSystem::SearchPath incpath = export_binfo.incpath;
+               if(!system && incpath.empty())
+                       incpath.push_back("include");
+               for(FS::Path &p: incpath)
+                       p = prefix/p;
+
+               for(const string &h: headers)
+                       all_found &= (builder.get_vfs().find_header(h, 0, incpath, system)!=0);
+
+               if(all_found)
+               {
+                       base_path = prefix;
+                       builder.get_logger().log("configure", "%s found in %s", name, ((system && use_pkgconfig) ? "system" : base_path.str()));
+                       break;
+               }
+       }
+
+       if(base_path.empty())
+       {
+               // TODO report which files were not found
+               builder.get_logger().log("problems", "Cannot locate files for %s", name);
+               problems.push_back("Cannot locate files");
+               return;
+       }
+
+       /* Add default entries to paths if they're empty and the package was found
+       in a non-system location */
+       if(!system && export_binfo.incpath.empty())
+               export_binfo.incpath.push_back(base_path/"include");
+       if(!system && export_binfo.libpath.empty())
+               export_binfo.libpath.push_back(base_path/"lib");
+
+       if(has_relative_paths)
+       {
+               for(FS::Path &p: export_binfo.incpath)
+                       p = base_path/p;
+               for(FS::Path &p: export_binfo.libpath)
+                       p = base_path/p;
+       }
+
+       if(!static_binfo.libs.empty())
+       {
+               VirtualFileSystem::SearchPath combined_libpath = static_binfo.libpath;
+               combined_libpath.insert(combined_libpath.end(), export_binfo.libpath.begin(), export_binfo.libpath.end());
+
+               for(const string &l: export_binfo.libs)
+                       if(Target *lib = builder.get_vfs().find_library(l, export_binfo.libpath, BuildInfo::FORCE_STATIC, system))
+                               if(StaticLibrary *stlib = dynamic_cast<StaticLibrary *>(lib))
+                               {
+                                       for(const string &s: static_binfo.libs)
+                                               stlib->add_required_library(s);
+                                       for(const FS::Path &p: combined_libpath)
+                                               stlib->add_library_path(p);
+                               }
+       }
+}
+
+
+BinaryPackage::Loader::Loader(BinaryPackage &p):
+       DataFile::DerivedObjectLoader<BinaryPackage, Package::Loader>(p)
+{
+       add("build_info", &Loader::build_info);
+       add("header",     &Loader::header);
+}
+
+void BinaryPackage::Loader::build_info()
+{
+       load_sub(obj.export_binfo);
+}
+
+void BinaryPackage::Loader::header(const string &h)
+{
+       obj.headers.push_back(h);
+}
diff --git a/source/lib/binarypackage.h b/source/lib/binarypackage.h
new file mode 100644 (file)
index 0000000..62a8acf
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef BINARYPACKAGE_H_
+#define BINARYPACKAGE_H_
+
+#include "package.h"
+
+/**
+Represents a package that is installed on the system, but can't be built by
+Builder.
+*/
+class BinaryPackage: public Package
+{
+public:
+       class Loader: public Msp::DataFile::DerivedObjectLoader<BinaryPackage, Package::Loader>
+       {
+       public:
+               Loader(BinaryPackage &);
+       private:
+               void build_info();
+               void header(const std::string &);
+       };
+
+       using Flags = std::vector<std::string>;
+
+private:
+       Msp::FS::Path base_path;
+       std::vector<std::string> headers;
+       BuildInfo static_binfo;
+
+public:
+       BinaryPackage(Builder &, const std::string &);
+
+       const BuildInfo &get_static_build_info() const { return static_binfo; }
+
+       static BinaryPackage *from_flags(Builder &, const std::string &, const Flags &, const Flags & = Flags());
+private:
+       static void process_flags(const Flags &, BuildInfo &);
+       void do_prepare() override;
+};
+
+#endif
diff --git a/source/lib/booleanevaluator.cpp b/source/lib/booleanevaluator.cpp
new file mode 100644 (file)
index 0000000..b7c399f
--- /dev/null
@@ -0,0 +1,148 @@
+#include <stdexcept>
+#include <msp/strings/format.h>
+#include "booleanevaluator.h"
+
+using namespace std;
+using namespace Msp;
+
+BooleanEvaluator::BooleanEvaluator(const ValueFunction &f):
+       func([f](const string &value, const string *){ return f(value); }),
+       ops("&|!")
+{ }
+
+BooleanEvaluator::BooleanEvaluator(const CompareFunction &f):
+       func(f),
+       ops("&|!=^")
+{ }
+
+bool BooleanEvaluator::evaluate(const string &str)
+{
+       string buf;
+       last_was_op = true;
+       for(auto i=str.begin();; ++i)
+       {
+               if(i!=str.end() && (isalnum(*i) || (!buf.empty() && (*i=='_' || *i=='-'))))
+                       buf += *i;
+               else
+               {
+                       if(!buf.empty())
+                       {
+                               if(!last_was_op)
+                                       throw runtime_error("syntax error at "+buf);
+
+                               char op = (op_stack.empty() ? 0 : op_stack.back());
+                               if(op=='=' || op=='^')
+                               {
+                                       op_stack.pop_back();
+                                       string var = var_stack.back();
+                                       var_stack.pop_back();
+                                       bool value = (func(var, &buf) == (op=='='));
+                                       value_stack.push_back(value);
+                               }
+                               else
+                                       var_stack.push_back(buf);
+
+                               buf.clear();
+                               last_was_op = false;
+                       }
+
+                       if(i==str.end())
+                               break;
+                       else if(isspace(*i))
+                               ;
+                       else if(ops.find(*i)!=string::npos)
+                               push_op(*i);
+                       else
+                               throw runtime_error(format("syntax error at %c", *i));
+               }
+       }
+
+       collapse(0);
+
+       bool value = pop_value();
+       if(!value_stack.empty())
+               throw runtime_error("too many values");
+
+       return value;
+}
+
+void BooleanEvaluator::push_op(char op)
+{
+       if(last_was_op!=is_unary(op))
+               throw runtime_error(format("syntax error at %c", op));
+       // TODO Disallow mixing of ! and =/^
+       if(is_logic(op) && !var_stack.empty())
+               value_stack.push_back(pop_value());
+
+       if(!is_unary(op))
+               collapse(precedence(op));
+       op_stack.push_back(op);
+       last_was_op = true;
+}
+
+bool BooleanEvaluator::pop_value()
+{
+       if(!var_stack.empty())
+       {
+               string var = var_stack.back();
+               var_stack.pop_back();
+               return func(var, 0);
+       }
+       else if(!value_stack.empty())
+       {
+               bool value = value_stack.back();
+               value_stack.pop_back();
+               return value;
+       }
+
+       throw runtime_error("value stack underflow");
+}
+
+void BooleanEvaluator::collapse(unsigned until)
+{
+       while(!op_stack.empty())
+       {
+               char op = op_stack.back();
+               if(precedence(op)<until)
+                       return;
+
+               op_stack.pop_back();
+               bool value1 = pop_value();
+               if(is_unary(op))
+               {
+                       if(op=='!')
+                               value1 = !value1;
+               }
+               else
+               {
+                       bool value2 = pop_value();
+                       if(op=='&')
+                               value1 = (value1 && value2);
+                       else if(op=='|')
+                               value1 = (value1 || value2);
+               }
+               value_stack.push_back(value1);
+       }
+}
+
+unsigned BooleanEvaluator::precedence(char op)
+{
+       if(op=='&')
+               return 1;
+       else if(op=='=' || op=='^')
+               return 2;
+       else if(op=='!')
+               return 3;
+       else
+               return 0;
+}
+
+bool BooleanEvaluator::is_unary(char op)
+{
+       return op=='!';
+}
+
+bool BooleanEvaluator::is_logic(char op)
+{
+       return (op=='&' || op=='|' || op=='!');
+}
diff --git a/source/lib/booleanevaluator.h b/source/lib/booleanevaluator.h
new file mode 100644 (file)
index 0000000..c3f61f2
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef BOOLEANEVALUATOR_H_
+#define BOOLEANEVALUATOR_H_
+
+#include <functional>
+#include <string>
+#include <vector>
+
+class BooleanEvaluator
+{
+public:
+       using ValueFunction = std::function<bool(const std::string &)>;
+       using CompareFunction = std::function<bool(const std::string &, const std::string *)>;
+
+private:
+       CompareFunction func;
+       std::string ops;
+       std::vector<std::string> var_stack;
+       std::vector<unsigned char> value_stack;
+       std::vector<char> op_stack;
+       bool last_was_op;
+
+public:
+       BooleanEvaluator(const ValueFunction &);
+       BooleanEvaluator(const CompareFunction &);
+
+       bool evaluate(const std::string &);
+private:
+       void push_op(char);
+       bool pop_value();
+       void collapse(unsigned);
+       unsigned precedence(char);
+       bool is_unary(char);
+       bool is_logic(char);
+};
+
+#endif
diff --git a/source/lib/builder.cpp b/source/lib/builder.cpp
new file mode 100644 (file)
index 0000000..92924eb
--- /dev/null
@@ -0,0 +1,364 @@
+#include <deque>
+#include <set>
+#include <msp/core/algorithm.h>
+#include <msp/core/except.h>
+#include <msp/core/maputils.h>
+#include <msp/datafile/parser.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/utils.h>
+#include <msp/io/buffered.h>
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include <msp/strings/format.h>
+#include <msp/time/timedelta.h>
+#include <msp/time/utils.h>
+#include "android/androidtools.h"
+#include "binarypackage.h"
+#include "builder.h"
+#include "builtin/builtintools.h"
+#include "clang/clangtools.h"
+#include "datafile/datatool.h"
+#include "gnu/gnutools.h"
+#include "installedfile.h"
+#include "msvc/microsofttools.h"
+#include "package.h"
+#include "sharedlibrary.h"
+#include "sourcepackage.h"
+#include "task.h"
+#include "virtualtarget.h"
+
+using namespace std;
+using namespace Msp;
+
+Builder::Builder():
+       package_manager(*this),
+       native_arch(*this, string()),
+       vfs(*this),
+       build_graph(*this),
+       logger(&default_logger)
+{
+       set_architecture(string());
+}
+
+Builder::~Builder()
+{
+       if(current_arch!=&native_arch)
+               delete current_arch;
+}
+
+void Builder::set_architecture(const string &name)
+{
+       if(current_arch!=&native_arch)
+               delete current_arch;
+
+       if(name.empty())
+               current_arch = &native_arch;
+       else
+               current_arch = new Architecture(*this, name);
+
+       if(auto_prefix)
+               update_auto_prefix();
+}
+
+vector<string> Builder::get_build_types() const
+{
+       vector<string> keys;
+       keys.reserve(build_types.size());
+       for(const auto &kvp: build_types)
+               keys.push_back(kvp.first);
+       return keys;
+}
+
+const BuildType &Builder::get_build_type() const
+{
+       if(!build_type)
+               throw invalid_state("no build type");
+       return *build_type;
+}
+
+void Builder::set_build_type(const string &name)
+{
+       build_type = &get_item(build_types, name);
+}
+
+void Builder::set_prefix(const FS::Path &p)
+{
+       auto_prefix = false;
+       prefix = p;
+}
+
+void Builder::set_temp_directory(const FS::Path &p)
+{
+       tempdir = p;
+}
+
+void Builder::update_auto_prefix()
+{
+       if(current_arch->is_native())
+               prefix = FS::get_home_dir()/"local";
+       else
+               prefix = FS::get_home_dir()/"local"/current_arch->get_name();
+}
+
+void Builder::add_default_tools()
+{
+       toolchain.add_toolchain(new GnuTools(*this, *current_arch));
+       toolchain.add_toolchain(new ClangTools(*this, *current_arch));
+       if(current_arch->get_system()=="android")
+               toolchain.add_toolchain(new AndroidTools(*this, *current_arch));
+       if(current_arch->get_system()=="windows")
+               toolchain.add_toolchain(new MicrosoftTools(*this, *current_arch));
+       toolchain.add_toolchain(new BuiltinTools(*this));
+       toolchain.add_tool(new DataTool(*this));
+
+       auto i = find_if(toolchain.get_toolchains(), [](const Toolchain *tc){ return (tc->has_tool("CC") || tc->has_tool("CXX")); });
+       if(i!=toolchain.get_toolchains().end())
+       {
+               current_arch->refine((*i)->get_name());
+               if(auto_prefix)
+                       update_auto_prefix();
+       }
+}
+
+void Builder::set_logger(const Logger *l)
+{
+       logger = (l ? l : &default_logger);
+}
+
+vector<string> Builder::collect_problems() const
+{
+       vector<string> problems;
+       set<const Package *> broken_packages;
+       set<const Component *> broken_components;
+       set<const Tool *> broken_tools;
+
+       for(const auto &kvp: build_graph.get_targets())
+               if(kvp.second->is_broken())
+               {
+                       for(const string &p: kvp.second->get_problems())
+                               problems.push_back(format("%s: %s", kvp.second->get_name(), p));
+
+                       const Package *package = kvp.second->get_package();
+                       if(package && !package->get_problems().empty())
+                               broken_packages.insert(package);
+
+                       const Component *component = kvp.second->get_component();
+                       if(component && !component->get_problems().empty())
+                               broken_components.insert(component);
+
+                       const Tool *tool = kvp.second->get_tool();
+                       if(tool && !tool->get_problems().empty())
+                               broken_tools.insert(tool);
+               }
+
+       // TODO Sort components after their packages, and targets last
+       for(const Package *p: broken_packages)
+               for(const string &b: p->get_problems())
+                       problems.push_back(format("%s: %s", p->get_name(), b));
+
+       for(const Component *c: broken_components)
+               for(const string &b: c->get_problems())
+                       problems.push_back(format("%s/%s: %s", c->get_package().get_name(), c->get_name(), b));
+
+       for(const Tool *t: broken_tools)
+               for(const string &b: t->get_problems())
+                       problems.push_back(format("%s: %s", t->get_tag(), b));
+
+       return problems;
+}
+
+void Builder::load_build_file(const FS::Path &fn, const Config::InputOptions *opts, bool all)
+{
+       IO::BufferedFile in(fn.str());
+
+       get_logger().log("files", "Reading %s", fn);
+
+       DataFile::Parser parser(in, fn.str());
+       Loader loader(*this, opts, all);
+       loader.load(parser);
+}
+
+void Builder::save_caches()
+{
+       for(const auto &kvp: package_manager.get_packages())
+               kvp.second->save_caches();
+}
+
+int Builder::build(unsigned jobs, bool dry_run, bool show_progress)
+{
+       unsigned total = build_graph.count_rebuild_targets();
+
+       if(!total)
+       {
+               get_logger().log("summary", "Already up to date");
+               return 0;
+       }
+       get_logger().log("summary", "Will build %d target%s", total, (total!=1 ? "s" : ""));
+
+       vector<Task *> tasks;
+
+       unsigned count = 0;
+
+       bool fail = false;
+       bool finish = false;
+       bool starved = false;
+
+       while(!finish)
+       {
+               if(tasks.size()<jobs && !fail && !starved)
+               {
+                       Target *tgt = build_graph.get_buildable_target();
+                       if(tgt)
+                       {
+                               if(tgt->get_tool())
+                               {
+                                       if(show_progress)
+                                               IO::print("\033[K");
+                                       get_logger().log("tasks", "%-4s  %s", tgt->get_tool()->get_tag(), tgt->get_name());
+                               }
+                               Task *task = tgt->build();
+                               if(task)
+                               {
+                                       get_logger().log("commands", "%s", task->get_command());
+                                       if(dry_run)
+                                       {
+                                               task->signal_finished.emit(true);
+                                               delete task;
+                                       }
+                                       else
+                                       {
+                                               task->start();
+                                               tasks.push_back(task);
+                                       }
+                               }
+
+                               if(show_progress)
+                                       IO::print("%d of %d target%s built\033[1G", count, total, (total!=1 ? "s" : ""));
+                       }
+                       else if(tasks.empty())
+                               finish = true;
+                       else
+                               starved = true;
+               }
+               else
+                       Time::sleep(10*Time::msec);
+
+               for(unsigned i=0; i<tasks.size();)
+               {
+                       Task::Status status;
+                       if(jobs==1 || (tasks.size()==1 && starved))
+                               status = tasks[i]->wait();
+                       else
+                               status = tasks[i]->check();
+
+                       if(status!=Task::RUNNING)
+                       {
+                               ++count;
+
+                               delete tasks[i];
+                               tasks.erase(tasks.begin()+i);
+                               if(status==Task::ERROR)
+                                       fail = true;
+                               if(tasks.empty() && fail)
+                                       finish = true;
+                               starved = false;
+                       }
+                       else
+                               ++i;
+               }
+       }
+
+       if(show_progress)
+               IO::print("\033[K");
+       if(fail)
+               get_logger().log("summary", "Build failed");
+       else if(show_progress)
+               get_logger().log("summary", "Build complete");
+
+       return fail;
+}
+
+int Builder::clean(bool all, bool dry_run)
+{
+       // Cleaning doesn't care about ordering, so a simpler method can be used
+
+       set<Target *> clean_tgts;
+       deque<Target *> queue;
+       queue.push_back(&build_graph.get_goals());
+
+       while(!queue.empty())
+       {
+               Target *tgt = queue.front();
+               queue.pop_front();
+
+               if(tgt->is_buildable() && (tgt->get_package()==&package_manager.get_main_package() || all))
+                       clean_tgts.insert(tgt);
+
+               for(Target *t: tgt->get_dependencies())
+                       if(!clean_tgts.count(t))
+                               queue.push_back(t);
+       }
+
+       for(Target *t: clean_tgts)
+       {
+               get_logger().log("tasks", "RM    %s", t->get_name());
+               if(!dry_run)
+                       t->clean();
+       }
+
+       return 0;
+}
+
+
+Builder::Loader::Loader(Builder &b, const Config::InputOptions *o, bool a):
+       DataFile::ObjectLoader<Builder>(b),
+       options(o),
+       conf_all(a)
+{
+       add("architecture", &Loader::architecture);
+       add("binary_package", &Loader::binpkg);
+       add("build_type", &Loader::build_type);
+       add("package", &Loader::package);
+
+       if(!obj.top_loader)
+               obj.top_loader = this;
+       else if(!options && obj.top_loader!=this && obj.top_loader->conf_all)
+               options = obj.top_loader->options;
+}
+
+Builder::Loader::~Loader()
+{
+       if(obj.top_loader==this)
+               obj.top_loader = 0;
+}
+
+void Builder::Loader::architecture(const string &n)
+{
+       if(obj.current_arch->match_name(n))
+               load_sub(*obj.current_arch);
+}
+
+void Builder::Loader::binpkg(const string &n)
+{
+       BinaryPackage *pkg = new BinaryPackage(obj, n);
+       load_sub(*pkg);
+}
+
+void Builder::Loader::build_type(const string &n)
+{
+       BuildType btype(n);
+       load_sub(btype);
+       auto i = obj.build_types.insert({ n, btype }).first;
+       if(!obj.build_type)
+               obj.build_type = &i->second;
+}
+
+void Builder::Loader::package(const string &n)
+{
+       SourcePackage *pkg = new SourcePackage(obj, n, get_source());
+
+       load_sub(*pkg, options);
+
+       if(obj.build_type)
+               pkg->set_build_type(*obj.build_type);
+}
diff --git a/source/lib/builder.h b/source/lib/builder.h
new file mode 100644 (file)
index 0000000..e06cec3
--- /dev/null
@@ -0,0 +1,113 @@
+#ifndef BUILDER_H_
+#define BUILDER_H_
+
+#include <map>
+#include <string>
+#include <msp/datafile/loader.h>
+#include <msp/fs/path.h>
+#include "architecture.h"
+#include "buildgraph.h"
+#include "buildtype.h"
+#include "config.h"
+#include "logger.h"
+#include "packagemanager.h"
+#include "target.h"
+#include "toolchain.h"
+#include "virtualfilesystem.h"
+
+class FileTarget;
+class Package;
+class SourcePackage;
+
+/**
+This class ties everything else together.  It also contains code for loading
+build files and supervising the build process.
+*/
+class Builder
+{
+private:
+       class Loader: public Msp::DataFile::ObjectLoader<Builder>
+       {
+       private:
+               const Config::InputOptions *options;
+               bool conf_all;
+
+       public:
+               Loader(Builder &, const Config::InputOptions * = 0, bool = false);
+               ~Loader();
+
+       private:
+               void architecture(const std::string &);
+               void binpkg(const std::string &);
+               void build_type(const std::string &);
+               void package(const std::string &);
+       };
+
+private:
+       PackageManager package_manager;
+
+       Architecture native_arch;
+       Architecture *current_arch = 0;
+       std::map<std::string, BuildType> build_types;
+       BuildType *build_type = 0;
+       Toolchain toolchain;
+       VirtualFileSystem vfs;
+       BuildGraph build_graph;
+       Logger default_logger;
+       const Logger *logger;
+
+       bool auto_prefix = true;
+       Msp::FS::Path prefix;
+       Msp::FS::Path tempdir = "temp";
+
+       Loader *top_loader = 0;
+
+public:
+       Builder();
+       ~Builder();
+
+       PackageManager &get_package_manager() { return package_manager; }
+
+       void set_architecture(const std::string &);
+       const Architecture &get_current_arch() const { return *current_arch; }
+       const Architecture &get_native_arch() const { return native_arch; }
+       void set_build_type(const std::string &);
+       std::vector<std::string> get_build_types() const;
+       const BuildType &get_build_type() const;
+       BuildGraph &get_build_graph() { return build_graph; }
+       void set_prefix(const Msp::FS::Path &);
+       void set_temp_directory(const Msp::FS::Path &);
+       const Msp::FS::Path &get_prefix() const { return prefix; }
+       const Msp::FS::Path &get_temp_directory() const { return tempdir; }
+
+private:
+       void update_auto_prefix();
+
+public:
+       void add_default_tools();
+       const Toolchain &get_toolchain() const { return toolchain; }
+       VirtualFileSystem &get_vfs() { return vfs; }
+       void set_logger(const Logger *);
+       const Logger &get_logger() const { return *logger; }
+
+       std::vector<std::string> collect_problems() const;
+
+       /** Loads a build file.  If opts is not null, it is used to configure any
+       packages loaded from this file.  If all is true, external packages are also
+       configured. */
+       void load_build_file(const Msp::FS::Path &, const Config::InputOptions *opts = 0, bool all = false);
+
+       /** Saves package configuration and dependency caches. */
+       void save_caches();
+
+       /** Builds the goal targets.  The build graph must be prepared first. */
+       int build(unsigned jobs = 1, bool dry_run = false, bool show_progress = false);
+
+       /** Cleans buildable targets.  If all is true, cleans all packages.
+       Otherwise cleans only the default package. */
+       int clean(bool all = false, bool dry_run = false);
+
+       int do_create_makefile();
+};
+
+#endif
diff --git a/source/lib/buildgraph.cpp b/source/lib/buildgraph.cpp
new file mode 100644 (file)
index 0000000..070f934
--- /dev/null
@@ -0,0 +1,89 @@
+#include "builder.h"
+#include "buildgraph.h"
+#include "component.h"
+#include "sourcepackage.h"
+#include "tool.h"
+#include "virtualtarget.h"
+
+using namespace std;
+
+BuildGraph::BuildGraph(Builder &b):
+       builder(b),
+       goals(new VirtualTarget(builder, "goals"))
+{
+       Target *world = new VirtualTarget(builder, "world");
+       world->add_dependency(*new VirtualTarget(builder, "default"));
+       world->add_dependency(*new VirtualTarget(builder, "install"));
+       world->add_dependency(*new VirtualTarget(builder, "archives"));
+}
+
+BuildGraph::~BuildGraph()
+{
+       for(const auto &kvp: targets)
+               delete kvp.second;
+}
+
+Target *BuildGraph::get_target(const string &n) const
+{
+       auto i = targets.find(n);
+       if(i!=targets.end())
+               return i->second;
+       return 0;
+}
+
+void BuildGraph::add_target(Target *t)
+{
+       targets.insert({ t->get_name(), t });
+}
+
+void BuildGraph::add_primary_target(Target &t)
+{
+       get_target("world")->add_dependency(t);
+
+       Package *main_pkg = &builder.get_package_manager().get_main_package();
+       if(t.get_package()==main_pkg && t.get_component() && t.get_component()->is_default())
+               get_target("default")->add_dependency(t);
+}
+
+void BuildGraph::add_installed_target(Target &t)
+{
+       Target *inst_tgt = 0;
+       if(const Tool *tool = t.get_tool())
+               inst_tgt = tool->create_install(t);
+       if(!inst_tgt)
+               inst_tgt = builder.get_toolchain().get_tool("CP").create_target(t);
+       get_target("install")->add_dependency(*inst_tgt);
+}
+
+void BuildGraph::add_goal(Target &t)
+{
+       goals->add_dependency(t);
+}
+
+void BuildGraph::prepare()
+{
+       if(goals->get_dependencies().empty())
+               add_goal(*get_target("default"));
+       goals->prepare();
+}
+
+void BuildGraph::force_full_rebuild()
+{
+       for(const auto &kvp: targets)
+               if(kvp.second->is_buildable() && !kvp.second->needs_rebuild())
+                       kvp.second->force_rebuild();
+}
+
+unsigned BuildGraph::count_rebuild_targets() const
+{
+       unsigned count = 0;
+       for(const auto &kvp: targets)
+               if(kvp.second->is_buildable() && kvp.second->needs_rebuild())
+                       ++count;
+       return count;
+}
+
+Target *BuildGraph::get_buildable_target() const
+{
+       return goals->get_buildable_target();
+}
diff --git a/source/lib/buildgraph.h b/source/lib/buildgraph.h
new file mode 100644 (file)
index 0000000..371b544
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef BUILDGRAPH_H_
+#define BUILDGRAPH_H_
+
+#include <map>
+#include <string>
+
+class Builder;
+class Target;
+
+/**
+Manages a graph of targets.
+*/
+class BuildGraph
+{
+private:
+       Builder &builder;
+       std::map<std::string, Target *> targets;
+       Target *goals;
+
+public:
+       BuildGraph(Builder &);
+       ~BuildGraph();
+
+       /** Looks up a target by name.  Returns 0 if no such target exists. */
+       Target *get_target(const std::string &) const;
+
+       const std::map<std::string, Target *> &get_targets() const { return targets; }
+
+       /** Adds a target.  It can later be retrieved by name.  Called from Target
+       constructor. */
+       void add_target(Target *);
+
+       /** Adds a target that is a primary build goal.  Such targets will be added
+       as dependencies of the "world" virtual target.  If the target belongs to a
+       default component of the main package, it's also added to the "default"
+       virtual target. */
+       void add_primary_target(Target &);
+
+       /** Adds a target that will be installed.  A new InstalledFile target is
+       created and added as a dependency to the "install" virtual target. */
+       void add_installed_target(Target &);
+
+       /** Adds a target as a toplevel goal.  These are stored as dependencies of
+       the "goals" virtual target. */
+       void add_goal(Target &);
+
+       Target &get_goals() const { return *goals; }
+
+       /** Prepares all toplevel goals for building.  If no goals are defined, the
+       "default" target is added as a goal. */
+       void prepare();
+
+       /** Marks all buildable targets to be rebuilt.  The graph must be prepared
+       first. */
+       void force_full_rebuild();
+
+       /** Returns the number of targets that are going to be rebuilt.  The graph
+       must be prepared first. */
+       unsigned count_rebuild_targets() const;
+
+       /** Returns a target that can be built and is needed for building the goal
+       targets.  Null */
+       Target *get_buildable_target() const;
+};
+
+#endif
diff --git a/source/lib/buildinfo.cpp b/source/lib/buildinfo.cpp
new file mode 100644 (file)
index 0000000..8e11670
--- /dev/null
@@ -0,0 +1,196 @@
+#include <msp/core/algorithm.h>
+#include <msp/strings/format.h>
+#include "buildinfo.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace {
+
+/** Removes any duplicate entries from a vector, leaving only the first one.
+The order of other elements is preserved. */
+template<typename T>
+void unique(vector<T> &v)
+{
+       vector<T> seen;
+       for(auto i=v.begin(); i!=v.end(); )
+       {
+               auto j = lower_bound(seen, *i);
+               if(j!=seen.end() && *j==*i)
+                       i = v.erase(i);
+               else
+                       seen.insert(j, *i++);
+       }
+}
+
+}
+
+
+BuildInfo::LibraryMode BuildInfo::get_libmode_for(const string &lib) const
+{
+       auto i = libmodes.find(lib);
+       if(i!=libmodes.end())
+               return i->second;
+       return libmode;
+}
+
+void BuildInfo::update_from(const BuildInfo &bi, UpdateLevel level)
+{
+       for(const auto &kvp: bi.defines)
+               defines[kvp.first] = kvp.second;
+       incpath.insert(incpath.begin(), bi.incpath.begin(), bi.incpath.end());
+       threads = bi.threads;
+
+       for(const auto &kvp: bi.standards)
+       {
+               auto j = standards.find(kvp.first);
+               if(j==standards.end())
+                       standards.insert(kvp);
+               else if(kvp.second.type!=j->second.type || kvp.second.year!=j->second.year)
+               {
+                       if(!kvp.second.type.compare(0, 3, "gnu"))
+                               j->second.type = kvp.second.type;
+                       if(kvp.second.year>j->second.year)
+                               j->second.year = kvp.second.year;
+               }
+       }
+
+       if(level!=CHAINED)
+       {
+               libpath.insert(libpath.begin(), bi.libpath.begin(), bi.libpath.end());
+               libs.insert(libs.begin(), bi.libs.begin(), bi.libs.end());
+       }
+
+       if(level==LOCAL)
+       {
+               sysroot = bi.sysroot;
+               local_incpath.insert(local_incpath.begin(), bi.local_incpath.begin(), bi.local_incpath.end());
+               libmode = bi.libmode;
+               rpath_mode = bi.rpath_mode;
+               for(const auto &kvp: bi.libmodes)
+                       libmodes[kvp.first] = kvp.second;
+               keep_symbols.insert(keep_symbols.end(), bi.keep_symbols.begin(), bi.keep_symbols.end());
+               debug = bi.debug;
+               optimize = bi.optimize;
+               strip = bi.strip;
+               warning_level = bi.warning_level;
+               fatal_warnings = bi.fatal_warnings;
+       }
+
+       unique(incpath);
+       unique(local_incpath);
+       unique(libpath);
+       unique(libs);
+       unique(keep_symbols);
+}
+
+
+BuildInfo::LanguageStandard::LanguageStandard(const string &std)
+{
+       auto i = find_if(std, [](char c){ return isdigit(static_cast<unsigned char>(c)); });
+       string::size_type num = i-std.begin();
+       type = std.substr(0, num);
+       year = lexical_cast<unsigned>(std.substr(num));
+       year += (year<70 ? 2000 : 1900);
+}
+
+string BuildInfo::LanguageStandard::str() const
+{
+       return format("%s%02d", type, year%100);
+}
+
+
+BuildInfo::Loader::Loader(BuildInfo &bi):
+       DataFile::ObjectLoader<BuildInfo>(bi)
+{
+       add("debug",    &BuildInfo::debug);
+       add("define",   &Loader::define);
+       add("incpath",  &Loader::incpath);
+       add("keep_symbol", &Loader::keep_symbol);
+       add("libpath",  &Loader::libpath);
+       add("library",  &Loader::library);
+       add("libmode",  &BuildInfo::libmode);
+       add("libmode",  &Loader::libmode_for_lib);
+       add("local_incpath", &Loader::local_incpath);
+       add("optimize", &BuildInfo::optimize);
+       add("runtime_path_mode", &BuildInfo::rpath_mode);
+       add("standard", &Loader::standard);
+       add("strip",    &BuildInfo::strip);
+       add("sysroot",  &Loader::sysroot);
+       add("threads",  &BuildInfo::threads);
+       add("warning_level", &BuildInfo::warning_level);
+       add("fatal_warnings", &BuildInfo::fatal_warnings);
+}
+
+void BuildInfo::Loader::incpath(const string &s)
+{
+       obj.incpath.push_back(s);
+}
+
+void BuildInfo::Loader::define(const string &d, const string &v)
+{
+       obj.defines[d] = v;
+}
+
+void BuildInfo::Loader::keep_symbol(const string &s)
+{
+       obj.keep_symbols.push_back(s);
+}
+
+void BuildInfo::Loader::libmode_for_lib(const string &l, LibraryMode m)
+{
+       obj.libmodes[l] = m;
+}
+
+void BuildInfo::Loader::libpath(const string &s)
+{
+       obj.libpath.push_back(s);
+}
+
+void BuildInfo::Loader::library(const string &s)
+{
+       obj.libs.push_back(s);
+}
+
+void BuildInfo::Loader::local_incpath(const string &s)
+{
+       obj.local_incpath.push_back(s);
+}
+
+void BuildInfo::Loader::standard(DataFile::Symbol tag, const string &std)
+{
+       obj.standards[tag.name] = std;
+}
+
+void BuildInfo::Loader::sysroot(const string &s)
+{
+       obj.sysroot = s;
+}
+
+
+void operator>>(const LexicalConverter &conv, BuildInfo::LibraryMode &libmode)
+{
+       if(conv.get()=="FORCE_STATIC")
+               libmode = BuildInfo::FORCE_STATIC;
+       else if(conv.get()=="STATIC")
+               libmode = BuildInfo::STATIC;
+       else if(conv.get()=="DYNAMIC")
+               libmode = BuildInfo::DYNAMIC;
+       else if(conv.get()=="FORCE_DYNAMIC")
+               libmode = BuildInfo::FORCE_DYNAMIC;
+       else
+               throw lexical_error(format("Conversion of '%s' to LibraryMode", conv.get()));
+}
+
+
+void operator>>(const LexicalConverter &conv, BuildInfo::RuntimePathMode &rpath_mode)
+{
+       if(conv.get()=="NONE")
+               rpath_mode = BuildInfo::NO_RPATH;
+       else if(conv.get()=="RELATIVE")
+               rpath_mode = BuildInfo::RELATIVE;
+       else if(conv.get()=="ABSOLUTE")
+               rpath_mode = BuildInfo::ABSOLUTE;
+       else
+               throw lexical_error(format("Conversion of '%s' to RuntimePathMode", conv.get()));
+}
diff --git a/source/lib/buildinfo.h b/source/lib/buildinfo.h
new file mode 100644 (file)
index 0000000..26afabf
--- /dev/null
@@ -0,0 +1,122 @@
+#ifndef BUILDINFO_H_
+#define BUILDINFO_H_
+
+#include <string>
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include <msp/fs/path.h>
+
+/**
+Stores information about compiler command line parameters in a more abstract
+form.  Allows combining with other BuildInfos to support package dependencies.
+*/
+class BuildInfo
+{
+public:
+       enum LibraryMode
+       {
+               FORCE_STATIC,  //< Only accept static libraries
+               STATIC,        //< Prefer static libraries but accept dynamic as well
+               DYNAMIC,       //< Prefer dynamic libraries but accept static as well
+               FORCE_DYNAMIC  //< Only accept dynamic libraries
+       };
+
+       enum RuntimePathMode
+       {
+               NO_RPATH,  //< Do not record rpath in binaries
+               RELATIVE,  //< Record relative rpath in binaries
+               ABSOLUTE   //< Record absolute rpath in binaries
+       };
+
+       class Loader: public Msp::DataFile::ObjectLoader<BuildInfo>
+       {
+       public:
+               Loader(BuildInfo &);
+       private:
+               void incpath(const std::string &);
+               void define(const std::string &, const std::string &);
+               void keep_symbol(const std::string &);
+               void libmode_for_lib(const std::string &, LibraryMode);
+               void libpath(const std::string &);
+               void library(const std::string &);
+               void local_incpath(const std::string &);
+               void standard(Msp::DataFile::Symbol, const std::string &);
+               void sysroot(const std::string &);
+       };
+       
+       enum UpdateLevel
+       {
+               LOCAL,       //< Include all information
+               DEPENDENCY,  //< Include all but code generation options
+               CHAINED      //< Include only compilation options
+       };
+
+       struct LanguageStandard
+       {
+               std::string type;
+               unsigned year = 0;
+
+               LanguageStandard() = default;
+               LanguageStandard(const std::string &);
+
+               std::string str() const;
+       };
+
+       /**
+       A wrapper which tracks the set status of the wrapped variable.  A default
+       value may be provided in initialization without causing it to be treated as
+       set.  Assigning from a raw value flags the Tracked object as set.  Assigning
+       from another Tracked object will only change the value of the target if the
+       source is set.  
+       */
+       template<typename T>
+       class Tracked
+       {
+       public:
+               using LoadType = T;
+
+       private:
+               T value{};
+               bool set = false;
+
+       public:
+               Tracked() = default;
+               Tracked(T v): value(v) { }
+               Tracked(const Tracked &t) = default;
+               Tracked &operator=(const Tracked &v) { if(v.set) { value = v.value; set = true; } return *this; }
+
+               Tracked &operator=(const T &v) { value = v; set = true; return *this; }
+               operator const T &() const { return value; }
+       };
+
+       Tracked<Msp::FS::Path> sysroot;
+       std::map<std::string, std::string> defines;
+       std::vector<Msp::FS::Path> incpath;
+       std::vector<Msp::FS::Path> local_incpath;
+       std::vector<Msp::FS::Path> libpath;
+       std::vector<std::string> libs;
+       Tracked<LibraryMode> libmode = DYNAMIC;
+       Tracked<RuntimePathMode> rpath_mode = NO_RPATH;
+       std::map<std::string, LibraryMode> libmodes;
+       std::vector<std::string> keep_symbols;
+       std::map<std::string, LanguageStandard> standards;
+       Tracked<bool> threads = false;
+       Tracked<bool> debug = false;
+       Tracked<int> optimize = 0;
+       Tracked<bool> strip = false;
+       Tracked<unsigned> warning_level = 0;
+       Tracked<bool> fatal_warnings = false;
+
+       /** Returns the library mode for linking a particular library.  If no mode
+       has been specified for that library, the the global library mode is
+       returned. */
+       LibraryMode get_libmode_for(const std::string &) const;
+
+       /** Updates the BuildInfo from another one.  Lists are concatenated, with
+       the first occurrence of each item preserved.  Scalars are overwritten.
+       
+       The update level determines what information is updated. */
+       void update_from(const BuildInfo &, UpdateLevel = LOCAL);
+};
+
+#endif
diff --git a/source/lib/buildtype.cpp b/source/lib/buildtype.cpp
new file mode 100644 (file)
index 0000000..2485ca5
--- /dev/null
@@ -0,0 +1,15 @@
+#include "buildtype.h"
+
+using namespace std;
+using namespace Msp;
+
+BuildType::Loader::Loader(BuildType &b):
+       DataFile::ObjectLoader<BuildType>(b)
+{
+       add("build_info", &Loader::build_info);
+}
+
+void BuildType::Loader::build_info()
+{
+       load_sub(obj.build_info);
+}
diff --git a/source/lib/buildtype.h b/source/lib/buildtype.h
new file mode 100644 (file)
index 0000000..c16e8f9
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef BUILDTYPE_H_
+#define BUILDTYPE_H_
+
+#include <string>
+#include <msp/datafile/objectloader.h>
+#include "buildinfo.h"
+
+class BuildType
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<BuildType>
+       {
+       public:
+               Loader(BuildType &);
+
+       private:
+               void build_info();
+       };
+
+private:
+       std::string name;
+       BuildInfo build_info;
+
+public:
+       BuildType(const std::string &n): name(n) { }
+
+       const std::string &get_name() const { return name; }
+       const BuildInfo &get_build_info() const { return build_info; }
+};
+
+#endif
diff --git a/source/lib/cache.cpp b/source/lib/cache.cpp
new file mode 100644 (file)
index 0000000..6baacce
--- /dev/null
@@ -0,0 +1,153 @@
+#include <msp/core/maputils.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include <msp/strings/utils.h>
+#include "builder.h"
+#include "cache.h"
+#include "sourcepackage.h"
+#include "target.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace {
+
+unsigned read_count(IO::Base &in)
+{
+       unsigned char head = in.get();
+       if((head&0xC0)==0xC0)
+       {
+               unsigned char tail = in.get();
+               return (head&0x3F)<<8 | tail;
+       }
+       else
+               return head;
+}
+
+string read_string(IO::Base &in)
+{
+       unsigned len = read_count(in);
+       char buf[0x4000];
+       len = in.read(buf, len);
+       return string(buf, len);
+}
+
+void write_count(IO::Base &out, unsigned count)
+{
+       if(count<0xC0)
+               out.put(count);
+       else if(count<0x4000)
+       {
+               out.put(0xC0 | (count>>8));
+               out.put(count&0xFF);
+       }
+       else
+               throw invalid_argument("write_count");
+}
+
+void write_string(IO::Base &out, const string &str)
+{
+       write_count(out, str.size());
+       out.write(str);
+}
+
+}
+
+
+Cache::Cache(SourcePackage &p):
+       package(p),
+       filename(package.get_temp_directory()/"../cache")
+{ }
+
+void Cache::set_value(const Target *tgt, const string &k, const string &v)
+{
+       Values vl;
+       vl.push_back(v);
+       set_values(tgt, k, vl);
+}
+
+void Cache::append_value(const Target *tgt, const string &k, const string &v)
+{
+       Key key(tgt->get_name(), k);
+       auto i = data.find(key);
+       if(i==data.end())
+               i = data.insert({ key, Values() }).first;
+       i->second.push_back(v);
+       changed = true;
+       package.get_builder().get_logger().log("cache", "Updated key %s %s+ %s", tgt->get_name(), k, v);
+}
+
+void Cache::set_values(const Target *tgt, const string &k, const Values &v)
+{
+       data[Key(tgt->get_name(), k)] = v;
+       changed = true;
+       package.get_builder().get_logger().log("cache", "Updated key %s %s: %s", tgt->get_name(), k, join(v.begin(), v.end()));
+}
+
+const string &Cache::get_value(const Target *tgt, const string &k)
+{
+       const Values &values = get_values(tgt, k);
+       if(values.empty())
+               throw logic_error("values.empty()");
+       return values.front();
+}
+
+const Cache::Values &Cache::get_values(const Target *tgt, const string &k)
+{
+       return get_item(data, Key(tgt->get_name(), k));
+}
+
+bool Cache::has_key(const Target *tgt, const string &k)
+{
+       return data.count(Key(tgt->get_name(), k));
+}
+
+void Cache::load()
+{
+       if(FS::Stat st = FS::stat(filename))
+       {
+               package.get_builder().get_logger().log("files", "Reading %s", filename);
+               IO::BufferedFile in(filename.str());
+
+               while(!in.eof())
+               {
+                       Key key;
+                       key.first = read_string(in);
+                       key.second = read_string(in);
+                       if(key.first.empty() || key.second.empty())
+                               break;
+                       Values &values = data[key];
+                       for(unsigned count = read_count(in); count; --count)
+                               values.push_back(read_string(in));
+                       package.get_builder().get_logger().log("cache", "Loaded key %s %s: %s", key.first, key.second, join(values.begin(), values.end()));
+               }
+
+               mtime = st.get_modify_time();
+       }
+}
+
+void Cache::save() const
+{
+       if(data.empty() || !changed)
+               return;
+
+       FS::Path dir = FS::dirname(filename);
+       if(!FS::exists(dir))
+               FS::mkpath(dir, 0755);
+       package.get_builder().get_logger().log("files", "Writing %s", filename);
+       IO::BufferedFile out(filename.str(), IO::M_WRITE);
+
+       for(const auto &kvp: data)
+       {
+               write_string(out, kvp.first.first);
+               write_string(out, kvp.first.second);
+               write_count(out, kvp.second.size());
+               for(const string &v: kvp.second)
+                       write_string(out, v);
+       }
+
+       changed = false;
+}
diff --git a/source/lib/cache.h b/source/lib/cache.h
new file mode 100644 (file)
index 0000000..9193dac
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef CACHE_H_
+#define CACHE_H_
+
+#include <map>
+#include <vector>
+#include <msp/fs/path.h>
+#include <msp/time/timestamp.h>
+
+class SourcePackage;
+class Target;
+
+/**
+Stores data between build runs.  This can be used to avoid scanning files again
+every time builder is run, or to detect outside changes.
+
+Data is stored as lists of strings and keyed to target and an arbitrary
+identifier.  Any kind of strings can be stored, even ones that contain
+unprintable characters or nuls.
+*/
+class Cache
+{
+public:
+       using Values = std::vector<std::string>;
+private:
+       using Key = std::pair<std::string, std::string>;
+
+       SourcePackage &package;
+       Msp::FS::Path filename;
+       std::map<Key, Values> data;
+       Msp::Time::TimeStamp mtime;
+       mutable bool changed = false;
+
+public:
+       Cache(SourcePackage &p);
+
+       /// Sets a key to a single value, replacing any existing values.
+       void set_value(const Target *, const std::string &, const std::string &);
+
+       /// Appends a value to a key.  If the key does not exist, it is created.
+       void append_value(const Target *, const std::string &, const std::string &);
+
+       /// Sets a key to a list of values, replacing any existing values.
+       void set_values(const Target *, const std::string &, const Values &);
+
+       /** Returns the first value from a key.  The key must exist and be
+       non-empty. */
+       const std::string &get_value(const Target *, const std::string &);
+
+       /// Returns the values from a key.  The key must exist.
+       const Values &get_values(const Target *, const std::string &);
+
+       /// Indicates whether a key exists.
+       bool has_key(const Target *, const std::string &);
+
+       /// Returns the last modification timestamp of the cache.
+       const Msp::Time::TimeStamp &get_mtime() const { return mtime; }
+
+       /** Loads the cache file and sets the last modification timestamp
+       accordingly. */
+       void load();
+
+       /** Saves the cache.  Does nothing if there is no data to save or nothing
+       has changed. */
+       void save() const;
+};
+
+#endif
diff --git a/source/lib/chainedtask.cpp b/source/lib/chainedtask.cpp
new file mode 100644 (file)
index 0000000..a232ef8
--- /dev/null
@@ -0,0 +1,66 @@
+#include <msp/strings/utils.h>
+#include "chainedtask.h"
+
+using namespace std;
+using namespace Msp;
+
+ChainedTask::~ChainedTask()
+{
+       for(Task *t: tasks)
+               delete t;
+}
+
+void ChainedTask::add_task(Task *t)
+{
+       tasks.push_back(t);
+}
+
+string ChainedTask::get_command() const
+{
+       string cmd;
+       for(Task *t: tasks)
+               append(cmd, "\n", t->get_command());
+       return cmd;
+}
+
+void ChainedTask::start()
+{
+       prepare();
+
+       current = 0;
+       tasks[current]->start();
+}
+
+Task::Status ChainedTask::check()
+{
+       while(current<tasks.size() && !process(tasks[current]->check())) ;
+
+       return final_status;
+}
+
+Task::Status ChainedTask::wait()
+{
+       while(current<tasks.size() && !process(tasks[current]->wait())) ;
+
+       return final_status;
+}
+
+bool ChainedTask::process(Status sub_status)
+{
+       if(sub_status==SUCCESS && current+1<tasks.size())
+       {
+               // The task succeeded and there's more to run
+               ++current;
+               tasks[current]->start();
+               return true;
+       }
+
+       if(sub_status!=RUNNING)
+       {
+               // The task is not running anymore and either failed or was the last one
+               current = tasks.size();
+               final_status = sub_status;
+       }
+
+       return false;
+}
diff --git a/source/lib/chainedtask.h b/source/lib/chainedtask.h
new file mode 100644 (file)
index 0000000..30a3b3e
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef CHAINEDTASK_H_
+#define CHAINEDTASK_H_
+
+#include <vector>
+#include "task.h"
+
+/**
+Runs multiple tasks as one unit, one after the other.  Execution of the chain
+will stop if any of the component tasks terminates with an error.
+*/
+class ChainedTask: public Task
+{
+private:
+       std::vector<Task *> tasks;
+       unsigned current = 0;
+       Status final_status = RUNNING;
+
+public:
+       ChainedTask(Task *t) { add_task(t); }
+       ~ChainedTask();
+
+       void add_task(Task *);
+
+       std::string get_command() const override;
+       void start() override;
+       Status check() override;
+       Status wait() override;
+private:
+       bool process(Status);
+};
+
+#endif
diff --git a/source/lib/component.cpp b/source/lib/component.cpp
new file mode 100644 (file)
index 0000000..4fddcef
--- /dev/null
@@ -0,0 +1,180 @@
+#include <deque>
+#include <msp/core/algorithm.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include "builder.h"
+#include "component.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+void Component::prepare()
+{
+       for(Package *r: requires)
+               r->prepare();
+}
+
+void Component::create_build_info()
+{
+       BuildInfo final_build_info;
+
+       const Package::Requirements &pkg_reqs = package.get_required_packages();
+       Package::Requirements direct_reqs = requires;
+       direct_reqs.insert(direct_reqs.end(), pkg_reqs.begin(), pkg_reqs.end());
+       for(Package *r: direct_reqs)
+               final_build_info.update_from(r->get_exported_build_info(), BuildInfo::DEPENDENCY);
+
+       Package::Requirements all_reqs = direct_reqs;
+       deque<Package *> queue(direct_reqs.begin(), direct_reqs.end());
+       while(!queue.empty())
+       {
+               Package *req = queue.front();
+               queue.pop_front();
+
+               for(Package *r: req->get_required_packages())
+                       if(!any_equals(all_reqs, r))
+                       {
+                               final_build_info.update_from(r->get_exported_build_info(), BuildInfo::CHAINED);
+                               all_reqs.push_back(r);
+                               queue.push_back(r);
+                       }
+       }
+
+       final_build_info.update_from(package.get_build_info());
+       final_build_info.update_from(build_info);
+       build_info = final_build_info;
+
+       for(FS::Path &p: build_info.incpath)
+               p = (package.get_source_directory()/p).str();
+       for(FS::Path &p: build_info.libpath)
+               p = (package.get_source_directory()/p).str();
+}
+
+BuildInfo Component::get_build_info_for_path(const FS::Path &path) const
+{
+       // XXX Cache these and check that the directories actually exist before adding them
+       BuildInfo binfo = build_info;
+
+       FS::Path gen_dir = package.get_temp_directory()/"generated";
+       if(FS::descendant_depth(path, gen_dir)>=0)
+       {
+               FS::Path subdir = FS::dirname(FS::relative(path, gen_dir));
+               binfo.local_incpath.push_back(package.get_source_directory()/subdir);
+       }
+       else
+       {
+               FS::Path subdir = FS::dirname(FS::relative(path, package.get_source_directory()));
+               binfo.local_incpath.push_back(gen_dir/subdir);
+       }
+
+       if(!overlays.empty())
+       {
+               FS::Path dir = FS::dirname(path);
+               string last = FS::basename(dir);
+               if(any_equals(overlays, last))
+                       dir = FS::dirname(dir);
+
+               if(any_equals(sources, dir))
+               {
+                       binfo.local_incpath.push_back(dir);
+                       for(const string &o: overlays)
+                               binfo.local_incpath.push_back(dir/o);
+               }
+       }
+       return binfo;
+}
+
+vector<FS::Path> Component::collect_source_files() const
+{
+       vector<FS::Path> files;
+       for(const FS::Path &p: sources)
+       {
+               if(FS::is_dir(p))
+               {
+                       vector<FS::Path> dirs;
+                       dirs.reserve(1+overlays.size());
+                       dirs.push_back(p);
+                       for(const string &o: overlays)
+                       {
+                               FS::Path opath = p/o;
+                               if(FS::is_dir(opath))
+                                       dirs.push_back(opath);
+                       }
+                       set<string> overlay_files;
+                       for(auto j=dirs.begin(); j!=dirs.end(); ++j)
+                       {
+                               package.get_builder().get_logger().log("files", "Traversing %s", *j);
+                               for(const string &f: list_files(*j))
+                               {
+                                       if(j!=dirs.begin())
+                                       {
+                                               if(overlay_files.count(f))
+                                                       continue;
+                                               overlay_files.insert(f);
+                                       }
+                                       FS::Path fn = *j/f;
+                                       if(!FS::is_dir(fn))
+                                               files.push_back(fn);
+                               }
+                       }
+               }
+               else
+               {
+                       files.push_back(p);
+                       for(const string &o: overlays)
+                       {
+                               FS::Path opath = FS::dirname(p)/o/FS::basename(p);
+                               if(FS::is_reg(opath))
+                                       files.push_back(opath);
+                       }
+               }
+       }
+
+       return files;
+}
+
+
+Component::Loader::Loader(Component &c):
+       DataFile::ObjectLoader<Component>(c),
+       ConditionalLoader(c.package, format("%s/%s", c.package.get_name(), c.name))
+{
+       add("overlay",         &Loader::overlay);
+       add("source",          &Loader::source);
+       add("install",         &Component::install);
+       add("install_map",     &Loader::install_map);
+       add("build_info",      &Loader::build_info);
+       add("require",         &Loader::require);
+       add("default",         &Component::deflt);
+}
+
+void Component::Loader::build_info()
+{
+       load_sub(obj.build_info);
+}
+
+void Component::Loader::install_map()
+{
+       load_sub(obj.install_map, obj.package.get_source_directory());
+}
+
+void Component::Loader::overlay(const string &o)
+{
+       obj.overlays.push_back(o);
+}
+
+void Component::Loader::require(const string &n)
+{
+       Package *req = obj.package.get_builder().get_package_manager().find_package(n);
+       if(req)
+               obj.requires.push_back(req);
+       else
+               obj.problems.push_back(format("Required package %s not found", n));
+}
+
+void Component::Loader::source(const string &s)
+{
+       obj.sources.push_back((obj.package.get_source_directory()/s).str());
+}
diff --git a/source/lib/component.h b/source/lib/component.h
new file mode 100644 (file)
index 0000000..7ddd972
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef COMPONENT_H_
+#define COMPONENT_H_
+
+#include <string>
+#include <msp/datafile/objectloader.h>
+#include <msp/fs/path.h>
+#include "buildinfo.h"
+#include "conditionalloader.h"
+#include "installmap.h"
+#include "package.h"
+
+class SourcePackage;
+
+/**
+Components specify things to be built.  Each component may build one binary (it
+may also build none), as well as install a bunch of headers.  Components inherit
+dependencies and build info from the package they belong to, and may also add
+their own.
+*/
+class Component
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<Component>, public ConditionalLoader
+       {
+       public:
+               Loader(Component &);
+       private:
+               void build_info();
+               void install_map();
+               void overlay(const std::string &);
+               void require(const std::string &);
+               void source(const std::string &);
+       };
+
+protected:
+       SourcePackage &package;
+       std::string name;
+       std::vector<Msp::FS::Path> sources;
+       std::vector<std::string> overlays;
+       bool install = false;
+       BuildInfo build_info;
+       Package::Requirements requires;
+       bool deflt = true;
+       InstallMap install_map;
+       std::vector<std::string> problems;
+
+       Component(SourcePackage &p, const std::string &n): package(p), name(n) { }
+public:
+       virtual ~Component() { }
+
+       const SourcePackage &get_package() const { return package; }
+       const std::string &get_name() const { return name; }
+
+       /** Returns a list of sources for the component.  They may refer to
+       directories or individual files. */
+       const std::vector<Msp::FS::Path> &get_sources() const { return sources; }
+
+       const std::vector<std::string> &get_overlays() const { return overlays; }
+
+protected:
+       /** Returns a list of all source files for the component. */
+       std::vector<Msp::FS::Path> collect_source_files() const;
+
+public:
+       bool get_install() const { return install; }
+       const InstallMap &get_install_map() const { return install_map; }
+       const Package::Requirements &get_required_packages() const { return requires; }
+       bool is_default() const { return deflt; }
+       const std::vector<std::string> &get_problems() const { return problems; }
+
+       /** Prepares any required packages. */
+       void prepare();
+
+       /** Prepares the build information for building.  Pulls build info from the
+       parent and dependency packages, and adds any component-specific flags. */
+       virtual void create_build_info();
+
+       virtual void update_exported_build_info(BuildInfo &) const { }
+
+       const BuildInfo &get_build_info() const { return build_info; }
+
+       BuildInfo get_build_info_for_path(const Msp::FS::Path &) const;
+
+       virtual void create_targets() const = 0;
+};
+
+#endif
diff --git a/source/lib/conditionalloader.cpp b/source/lib/conditionalloader.cpp
new file mode 100644 (file)
index 0000000..b9d9d25
--- /dev/null
@@ -0,0 +1,47 @@
+#include "booleanevaluator.h"
+#include "builder.h"
+#include "conditionalloader.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+ArchitectureConditional::ArchitectureConditional(const Builder &b, const string &l):
+       builder(b),
+       log_prefix(l)
+{
+       add("if_arch", &ArchitectureConditional::if_arch);
+}
+
+void ArchitectureConditional::if_arch(const string &cond)
+{
+       const Architecture &arch = builder.get_current_arch();
+       BooleanEvaluator eval([&arch](const string &value){ return arch.match_name(value); });
+       bool match = eval.evaluate(cond);
+       builder.get_logger().log("configure", "%s: arch %s %smatched", log_prefix, cond, (match ? "" : "not "));
+       if(match)
+               load_sub_with(*this);
+}
+
+
+FeatureConditional::FeatureConditional(const SourcePackage &p, const string &l):
+       package(p),
+       log_prefix(l)
+{
+       add("if_feature", &FeatureConditional::if_feature);
+}
+
+void FeatureConditional::if_feature(const string &cond)
+{
+       BooleanEvaluator eval([this](const string &feat, const string *value){ return package.match_feature(feat, value); });
+       bool match = eval.evaluate(cond);
+       package.get_builder().get_logger().log("configure", "%s: feature %s %smatched", log_prefix, cond, (match ? "" : "not "));
+       if(match)
+               load_sub_with(*this);
+}
+
+
+ConditionalLoader::ConditionalLoader(const SourcePackage &p, const string &l):
+       ArchitectureConditional(p.get_builder(), l),
+       FeatureConditional(p, l)
+{ }
diff --git a/source/lib/conditionalloader.h b/source/lib/conditionalloader.h
new file mode 100644 (file)
index 0000000..5184fac
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef CONDITIONALLOADER_H_
+#define CONDITIONALLOADER_H_
+
+#include <string>
+#include <msp/datafile/loader.h>
+
+class Builder;
+class SourcePackage;
+
+class ArchitectureConditional: virtual public Msp::DataFile::Loader
+{
+private:
+       const Builder &builder;
+       std::string log_prefix;
+
+protected:
+       ArchitectureConditional(const Builder &, const std::string &);
+
+private:
+       void if_arch(const std::string &);
+};
+
+
+class FeatureConditional: virtual public Msp::DataFile::Loader
+{
+private:
+       const SourcePackage &package;
+       std::string log_prefix;
+
+protected:
+       FeatureConditional(const SourcePackage &, const std::string &);
+
+       void if_feature(const std::string &);
+};
+
+
+class ConditionalLoader: public ArchitectureConditional, FeatureConditional
+{
+protected:
+       ConditionalLoader(const SourcePackage &, const std::string &);
+};
+
+#endif
diff --git a/source/lib/config.cpp b/source/lib/config.cpp
new file mode 100644 (file)
index 0000000..1d3b180
--- /dev/null
@@ -0,0 +1,109 @@
+#include <msp/core/maputils.h>
+#include <msp/datafile/writer.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include <msp/time/utils.h>
+#include "builder.h"
+#include "config.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+const Config::Option &Config::add_option(const Feature &f)
+{
+       Option opt(f);
+       auto i = pending_options.find(opt.name);
+       if(i!=pending_options.end())
+               opt.value = i->second;
+
+       return options.insert({ opt.name, opt }).first->second;
+}
+
+bool Config::set_option(const string &opt, const string &val)
+{
+       bool result = false;
+
+       auto i = options.find(opt);
+       if(i!=options.end())
+       {
+               if(i->second.value!=val)
+               {
+                       result = true;
+                       changed = true;
+                       mtime = Time::now();
+               }
+               i->second.value = val;
+       }
+
+       return result;
+}
+
+bool Config::is_option(const string &name) const
+{
+       return options.count(name);
+}
+
+const Config::Option &Config::get_option(const string &name) const
+{
+       return get_item(options, name);
+}
+
+void Config::load()
+{
+       FS::Path fn = package.get_source_directory()/".config";
+       FS::Stat stat = FS::stat(fn);
+       if(stat)
+       {
+               package.get_builder().get_logger().log("files", "Reading %s", fn);
+               IO::BufferedFile in(fn.str());
+
+               mtime = stat.get_modify_time();
+
+               DataFile::Parser parser(in, fn.str());
+               Loader loader(*this);
+               loader.load(parser);
+       }
+}
+
+void Config::save() const
+{
+       if(!changed)
+               return;
+
+       FS::Path fn = package.get_source_directory()/".config";
+
+       package.get_builder().get_logger().log("files", "Writing %s", fn);
+       IO::BufferedFile out(fn.str(), IO::M_WRITE);
+       DataFile::Writer writer(out);
+
+       for(const auto &kvp: options)
+               writer.write((DataFile::Statement("option"), kvp.second.name, kvp.second.value));
+
+       changed = false;
+}
+
+
+Config::Option::Option(const Feature &f):
+       Feature(f),
+       value(default_value)
+{
+       name = "with_"+name;
+}
+
+
+Config::Loader::Loader(Config &c):
+       DataFile::ObjectLoader<Config>(c)
+{
+       add("option", &Loader::option);
+}
+
+void Config::Loader::option(const string &n, const string &v)
+{
+       if(obj.options.count(n))
+               obj.set_option(n, v);
+       else
+               obj.pending_options[n] = v;
+}
diff --git a/source/lib/config.h b/source/lib/config.h
new file mode 100644 (file)
index 0000000..ba8416b
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef CONFIG_H_
+#define CONFIG_H_
+
+#include <map>
+#include <string>
+#include <msp/datafile/loader.h>
+#include <msp/fs/path.h>
+#include <msp/time/timestamp.h>
+#include "feature.h"
+
+class SourcePackage;
+
+/**
+Manages configuration for a package.  A configuration may have an arbitary
+amount of options, as well as a modification time (mtime).
+*/
+class Config
+{
+public:
+       /** A single configuration option. */
+       struct Option: public Feature
+       {
+               std::string value;
+
+               Option(const Feature &);
+       };
+
+       using InputOptions = std::map<std::string, std::string>;
+
+private:
+       class Loader: public Msp::DataFile::ObjectLoader<Config>
+       {
+       public:
+               Loader(Config &);
+       private:
+               void option(const std::string &, const std::string &);
+       };
+
+       SourcePackage &package;
+       std::map<std::string, Option> options;
+       InputOptions pending_options;
+       Msp::Time::TimeStamp mtime;
+       mutable bool changed = false;
+
+public:
+       Config(SourcePackage &p): package(p) { }
+
+       /** Adds a configuration option based on a feature. */
+       const Option &add_option(const Feature &);
+
+       bool set_option(const std::string &, const std::string &);
+
+       /** Checks whether an option exists. */
+       bool is_option(const std::string &) const;
+
+       /** Gets a configuration option by name. */
+       const Option &get_option(const std::string &) const;
+
+       const std::map<std::string, Option> &get_options() const { return options; }
+       const Msp::Time::TimeStamp &get_mtime() const { return mtime; }
+
+       void load();
+       void save() const;
+};
+
+#endif
diff --git a/source/lib/csourcefile.cpp b/source/lib/csourcefile.cpp
new file mode 100644 (file)
index 0000000..bc76605
--- /dev/null
@@ -0,0 +1,73 @@
+#include <msp/core/maputils.h>
+#include <msp/fs/utils.h>
+#include <msp/io/print.h>
+#include <msp/strings/regex.h>
+#include "builder.h"
+#include "component.h"
+#include "csourcefile.h"
+#include "sourcepackage.h"
+#include "tool.h"
+
+using namespace std;
+using namespace Msp;
+
+CSourceFile::CSourceFile(Builder &b, const Component &c, const FS::Path &p):
+       SourceFile(b, c, p)
+{
+       string ext = FS::extpart(FS::basename(path));
+       if(ext==".h" || ext==".H" || ext==".hpp")
+               install_location = FS::Path("include")/package->get_name();
+}
+
+void CSourceFile::parse_includes(IO::Base &in)
+{
+       static Regex r_include("^[ \t]*#include[ \t]+([\"<].*)[\">]");
+
+       string line;
+       while(in.getline(line))
+               if(RegMatch match = r_include.match(line))
+                       includes.push_back(match[1].str);
+}
+
+void CSourceFile::find_dependencies()
+{
+       if(!component || !mtime)
+               return;
+
+       const SourcePackage &spkg = component->get_package();
+
+       Cache &cache = spkg.get_cache();
+       if(mtime<cache.get_mtime() && cache.has_key(this, "includes"))
+               includes = cache.get_values(this, "includes");
+       else
+       {
+               IO::BufferedFile in(path.str());
+
+               builder.get_logger().log("files", "Reading includes from %s", path.str());
+
+               parse_includes(in);
+               cache.set_values(this, "includes", includes);
+       }
+
+       const BuildInfo &build_info = component->get_build_info_for_path(path);
+       const auto &incpath = build_info.incpath;
+       VirtualFileSystem::SearchPath local_incpath;
+       local_incpath.reserve(1+build_info.local_incpath.size()+incpath.size());
+       local_incpath.push_back(FS::dirname(path).str());
+       local_incpath.insert(local_incpath.end(), build_info.local_incpath.begin(), build_info.local_incpath.end());
+       local_incpath.insert(local_incpath.end(), incpath.begin(), incpath.end());
+
+       Tool *compiler = builder.get_toolchain().get_tool_for_suffix(FS::extpart(FS::basename(path)), true);
+       if(compiler)
+               compiler->prepare();
+       for(const string &i: includes)
+               if(Target *hdr = builder.get_vfs().find_header(i.substr(1), compiler, (i[0]=='"' ? local_incpath : incpath)))
+                       add_transitive_dependency(*hdr);
+}
+
+void CSourceFile::modified()
+{
+       includes.clear();
+       trans_depends.clear();
+       find_dependencies();
+}
diff --git a/source/lib/csourcefile.h b/source/lib/csourcefile.h
new file mode 100644 (file)
index 0000000..c57654a
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef CSOURCEFILE_H_
+#define CSOURCEFILE_H_
+
+#include <msp/io/base.h>
+#include "sourcefile.h"
+
+/**
+Represents a C or C++ source file.
+*/
+class CSourceFile: public SourceFile
+{
+protected:
+       std::vector<std::string> includes;
+
+public:
+       CSourceFile(Builder &b, const Msp::FS::Path &p): SourceFile(b, p) { }
+       CSourceFile(Builder &, const Component &, const Msp::FS::Path &);
+
+       const char *get_type() const override { return "CSourceFile"; }
+       const std::vector<std::string> &get_includes() const { return includes; }
+protected:
+       virtual void parse_includes(Msp::IO::Base &);
+       void find_dependencies() override;
+       void modified() override;
+};
+
+#endif
diff --git a/source/lib/customizedtool.cpp b/source/lib/customizedtool.cpp
new file mode 100644 (file)
index 0000000..9171996
--- /dev/null
@@ -0,0 +1,59 @@
+#include "builder.h"
+#include "customizedtool.h"
+#include "target.h"
+
+using namespace std;
+using namespace Msp;
+
+CustomizedTool::CustomizedTool(Builder &b, const std::string &t, const Architecture &a):
+       CustomizedTool(b.get_toolchain().get_tool(t), a)
+{ }
+
+CustomizedTool::CustomizedTool(Tool &t, const Architecture &a):
+       Tool(t.get_builder(), &a, t.get_tag()),
+       parent(t)
+{
+       input_suffixes = parent.get_input_suffixes();
+       aux_suffixes = parent.get_auxiliary_suffixes();
+       processing_unit = parent.get_processing_unit();
+
+       set_run([this](const Target &tgt){ return parent.run(tgt); });
+}
+
+Target *CustomizedTool::create_source(const Component &c, const FS::Path &p) const
+{
+       return parent.create_source(c, p);
+}
+
+Target *CustomizedTool::create_source(const FS::Path &p) const
+{
+       return parent.create_source(p);
+}
+
+Target *CustomizedTool::create_target(const vector<Target *> &s, const string &a)
+{
+       Target *target = parent.create_target(s, a);
+       target->set_tool(*this);
+       return target;
+}
+
+Target *CustomizedTool::create_install(Target &t) const
+{
+       return parent.create_install(t);
+}
+
+string CustomizedTool::create_build_signature(const BuildInfo &bi) const
+{
+       string sig = Tool::create_build_signature(bi);
+       string parent_sig = parent.create_build_signature(bi);
+       string::size_type comma = parent_sig.find(',');
+       if(comma==string::npos)
+               return sig;
+       else
+               return sig+parent_sig.substr(comma);
+}
+
+void CustomizedTool::do_prepare(ToolData &tool) const
+{
+       parent.prepare(static_cast<Tool &>(tool));
+}
diff --git a/source/lib/customizedtool.h b/source/lib/customizedtool.h
new file mode 100644 (file)
index 0000000..55c4c6a
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef CUSTOMIZEDTOOL_H_
+#define CUSTOMIZEDTOOL_H_
+
+#include "tool.h"
+
+class CustomizedTool: public Tool
+{
+protected:
+       Tool &parent;
+
+       CustomizedTool(Builder &, const std::string &, const Architecture &);
+       CustomizedTool(Tool &, const Architecture &);
+
+public:
+       const Tool *get_base_tool() const override { return &parent; }
+       Target *create_source(const Component &, const Msp::FS::Path &) const override;
+       Target *create_source(const Msp::FS::Path &) const override;
+       Target *create_target(const std::vector<Target *> &, const std::string & = std::string()) override;
+       Target *create_install(Target &) const override;
+       std::string create_build_signature(const BuildInfo &) const override;
+protected:
+       void do_prepare(ToolData &) const override;
+};
+
+#endif
diff --git a/source/lib/executable.cpp b/source/lib/executable.cpp
new file mode 100644 (file)
index 0000000..aa0d8fb
--- /dev/null
@@ -0,0 +1,13 @@
+#include "builder.h"
+#include "component.h"
+#include "executable.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+Executable::Executable(Builder &b, const Component &c, const vector<ObjectFile *> &objs):
+       Binary(b, c, b.get_current_arch().create_filename<Executable>(c.get_name()), objs)
+{
+       install_location = "bin";
+}
diff --git a/source/lib/executable.h b/source/lib/executable.h
new file mode 100644 (file)
index 0000000..5728e2f
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef EXECUTABLE_H_
+#define EXECUTABLE_H_
+
+#include "binary.h"
+
+class Executable: public Binary
+{
+public:
+       Executable(Builder &b, const Msp::FS::Path &p): Binary(b, p) { }
+       Executable(Builder &, const Component &, const std::vector<ObjectFile *> &);
+
+       const char *get_type() const override { return "Executable"; }
+};
+
+#endif
diff --git a/source/lib/exportdefinitions.cpp b/source/lib/exportdefinitions.cpp
new file mode 100644 (file)
index 0000000..7edf759
--- /dev/null
@@ -0,0 +1,20 @@
+#include "component.h"
+#include "exportdefinitions.h"
+#include "objectfile.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+ExportDefinitions::ExportDefinitions(Builder &b, const Component &c, const vector<ObjectFile *> &objs):
+       FileTarget(b, c.get_package(), generate_target_path(c))
+{
+       component = &c;
+       for(ObjectFile *o: objs)
+               add_dependency(*o);
+}
+
+FS::Path ExportDefinitions::generate_target_path(const Component &comp)
+{
+       return comp.get_package().get_temp_directory()/comp.get_name()/(comp.get_name()+".def");
+}
diff --git a/source/lib/exportdefinitions.h b/source/lib/exportdefinitions.h
new file mode 100644 (file)
index 0000000..b8631ee
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef EXPORTDEFINITIONS_H_
+#define EXPORTDEFINITIONS_H_
+
+#include "filetarget.h"
+
+class ObjectFile;
+
+/**
+An export definition file for a shared library.  Only used on Windows.
+*/
+class ExportDefinitions: public FileTarget
+{
+public:
+       ExportDefinitions(Builder &, const Component &, const std::vector<ObjectFile *> &);
+private:
+       static Msp::FS::Path generate_target_path(const Component &);
+
+public:
+       const char *get_type() const override { return "ExportDefinitions"; }
+};
+
+#endif
diff --git a/source/lib/externaltask.cpp b/source/lib/externaltask.cpp
new file mode 100644 (file)
index 0000000..37a36d3
--- /dev/null
@@ -0,0 +1,195 @@
+#include <cstdlib>
+#include <msp/fs/dir.h>
+#include <msp/io/console.h>
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include <msp/time/timedelta.h>
+#include "externaltask.h"
+
+using namespace std;
+using namespace Msp;
+
+ExternalTask::ExternalTask(const Arguments &a, const FS::Path &wd):
+       argv(a),
+       work_dir(wd)
+{
+       if(argv.empty())
+               throw invalid_argument("ExternalTask::ExternalTask");
+}
+
+ExternalTask::~ExternalTask()
+{
+       delete capture_pipe;
+}
+
+string ExternalTask::get_command() const
+{
+       string cmd;
+       for(const string &a: argv)
+       {
+               if(!cmd.empty())
+                       cmd += ' ';
+
+               for(char c: a)
+               {
+                       if(c=='"' || c=='\'' || c==' ' || c=='\\' || c=='&')
+                               cmd += '\\';
+                       cmd += c;
+               }
+       }
+
+       if(stdin_action==REDIRECT)
+       {
+               cmd += " <";
+               cmd += stdin_file.str();
+       }
+
+       if(stdout_action==REDIRECT)
+       {
+               cmd += " >";
+               cmd += stdout_file.str();
+       }
+
+       return cmd;
+}
+
+void ExternalTask::start()
+{
+       IO::File *devnull = 0;
+       IO::File *infile = 0;
+       IO::File *outfile = 0;
+
+       prepare();
+
+       process = new Process;
+
+       if(stdin_action==IGNORE || stdout_action==IGNORE || stderr_action==IGNORE)
+       {
+#ifdef _WIN32
+               devnull = new IO::File("nul", IO::M_RDWR);
+#else
+               devnull = new IO::File("/dev/null", IO::M_RDWR);
+#endif
+               if(stdin_action==IGNORE)
+                       process->redirect_cin(*devnull);
+               if(stdout_action==IGNORE)
+                       process->redirect_cout(*devnull);
+               if(stderr_action==IGNORE)
+                       process->redirect_cerr(*devnull);
+       }
+
+       if(stdout_action==REDIRECT)
+       {
+               outfile = new IO::File((work_dir/stdout_file).str(), IO::M_WRITE);
+               process->redirect_cout(*outfile);
+       }
+
+       if(stdout_action==CAPTURE || stderr_action==CAPTURE)
+       {
+               capture_pipe = new IO::Pipe;
+               if(stdout_action==CAPTURE)
+                       process->redirect_cout(*capture_pipe);
+               if(stderr_action==CAPTURE)
+                       process->redirect_cerr(*capture_pipe);
+       }
+
+       if(stdin_action==REDIRECT)
+       {
+               infile = new IO::File((work_dir/stdin_file).str());
+               process->redirect_cin(*infile);
+       }
+
+       if(!work_dir.empty())
+               process->set_working_directory(work_dir);
+
+       Process::Arguments args(argv.begin()+1, argv.end());
+       process->execute(argv.front(), args);
+       if(capture_pipe)
+               capture_pipe->set_mode(IO::M_READ);
+
+       delete devnull;
+       delete infile;
+       delete outfile;
+}
+
+Task::Status ExternalTask::check()
+{
+       return do_wait(false);
+}
+
+Task::Status ExternalTask::wait()
+{
+       return do_wait(true);
+}
+
+Task::Status ExternalTask::do_wait(bool block)
+{
+       while(process)
+       {
+               if(process->wait(block && !capture_pipe))
+               {
+                       exit_code = process->get_exit_code();
+                       delete process;
+                       process = 0;
+               }
+
+               // Do this after waiting to avoid a race condition
+               while(capture_pipe && IO::poll(*capture_pipe, IO::P_INPUT, 10*Time::msec))
+               {
+                       char buf[1024];
+                       unsigned len = capture_pipe->read(buf, sizeof(buf));
+                       if(len)
+                               output.append(buf, len);
+                       else
+                               break;
+               }
+
+               if(process)
+               {
+                       if(!block)
+                               return RUNNING;
+               }
+               else
+                       signal_finished.emit(!exit_code);
+       }
+
+       return exit_code ? ERROR : SUCCESS;
+}
+
+void ExternalTask::set_stdin(const FS::Path &f)
+{
+       stdin_action = REDIRECT;
+       stdin_file = f;
+}
+
+void ExternalTask::set_stdout(StreamAction a)
+{
+       if(a==REDIRECT)
+               throw invalid_argument("ExternalTask::set_stdout");
+       stdout_action = a;
+}
+
+void ExternalTask::set_stdout(const FS::Path &f)
+{
+       stdout_action = REDIRECT;
+       stdout_file = f;
+}
+
+void ExternalTask::set_stderr(StreamAction a)
+{
+       if(a==REDIRECT)
+               throw invalid_argument("ExternalTask::set_stdout");
+       stderr_action = a;
+}
+
+string ExternalTask::run_and_capture_output(const Arguments &argv, const FS::Path &wd, bool capture_stderr)
+{
+       ExternalTask task(argv, wd);
+       task.stdin_action = IGNORE;
+       task.set_stdout(CAPTURE);
+       task.set_stderr(capture_stderr ? CAPTURE : IGNORE);
+       task.start();
+       if(task.wait()!=SUCCESS)
+               throw runtime_error(format("%s failed", argv.front()));
+       return task.get_output();
+}
diff --git a/source/lib/externaltask.h b/source/lib/externaltask.h
new file mode 100644 (file)
index 0000000..9b9bc43
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef EXTERNALTASK_H_
+#define EXTERNALTASK_H_
+
+#include <string>
+#include <vector>
+#include <msp/core/process.h>
+#include <msp/fs/path.h>
+#include <msp/io/pipe.h>
+#include "task.h"
+
+/**
+Runs an external command.  A zero exit status is translated to a SUCCESS status
+for the task, and anything else is treated as an error.  Output can optionally
+be captured.
+*/
+class ExternalTask: public Task
+{
+public:
+       enum StreamAction
+       {
+               PASSTHROUGH,  //< Do not touch the stream
+               CAPTURE,      //< Capture the stream
+               REDIRECT,     //< Redirect the stream to/from a file
+               IGNORE        //< Redirect the stream to oblivion
+       };
+
+       using Arguments = Msp::Process::Arguments;
+
+private:
+       Arguments argv;
+       Msp::FS::Path work_dir;
+       Msp::Process *process = 0;
+       int exit_code = -1;
+       StreamAction stdin_action = PASSTHROUGH;
+       Msp::FS::Path stdin_file;
+       StreamAction stdout_action = PASSTHROUGH;
+       Msp::FS::Path stdout_file;
+       StreamAction stderr_action = PASSTHROUGH;
+       Msp::IO::Pipe *capture_pipe = 0;
+       std::string output;
+
+public:
+       /** Creates an ExternalTask with an argument array and an optional working
+       directory.  The first element of the argument array should be the command
+       name.  If the working directory is not specified, no chdir is done. */
+       ExternalTask(const Arguments &, const Msp::FS::Path & = Msp::FS::Path());
+
+       ~ExternalTask();
+
+       std::string get_command() const override;
+       void start() override;
+       Status check() override;
+       Status wait() override;
+private:
+       Status do_wait(bool);
+
+public:
+       /// Redirect stdin from a file.  Has no effect after the task is started.
+       void set_stdin(const Msp::FS::Path &);
+
+       /// Sets destination for stdout.  Has no effect after the task is started.
+       void set_stdout(StreamAction);
+
+       /// Redirect stdout to a file.  Has no effect after the task is started.
+       void set_stdout(const Msp::FS::Path &);
+
+       /// Sets destination for stderr.  Has no effect after the task is started.
+       void set_stderr(StreamAction);
+
+       /** Returns captured output, if any.  This may be called while the task is
+       still running, but it will always return all output. */
+       const std::string &get_output() const { return output; }
+
+       /** Executes a command and captures its output.  If the command exits with
+       a nonzero status, an exception is thrown. */
+       static std::string run_and_capture_output(const Arguments &, const Msp::FS::Path & = Msp::FS::Path(), bool = false);
+};
+
+#endif
diff --git a/source/lib/feature.cpp b/source/lib/feature.cpp
new file mode 100644 (file)
index 0000000..2518377
--- /dev/null
@@ -0,0 +1,20 @@
+#include "feature.h"
+
+using namespace std;
+using namespace Msp;
+
+Feature::Loader::Loader(Feature &f):
+       Msp::DataFile::ObjectLoader<Feature>(f)
+{
+       add("choice",      &Loader::choice);
+       add("description", &Feature::description);
+       add("default",     &Feature::default_value);
+       add("export",      &Feature::exported);
+}
+
+void Feature::Loader::choice(const string &c)
+{
+       if(obj.choices.empty())
+               obj.default_value = c;
+       obj.choices.push_back(c);
+}
diff --git a/source/lib/feature.h b/source/lib/feature.h
new file mode 100644 (file)
index 0000000..0dca1f5
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef FEATURE_H_
+#define FEATURE_H_
+
+#include <msp/datafile/objectloader.h>
+
+struct Feature
+{
+       class Loader: public Msp::DataFile::ObjectLoader<Feature>
+       {
+       public:
+               Loader(Feature &);
+
+       private:
+               void choice(const std::string &);
+       };
+
+       std::string name;
+       std::string description;
+       std::string default_value = "no";
+       std::vector<std::string> choices;
+       bool exported = false;
+
+       Feature(const std::string &n): name(n) { }
+};
+
+#endif
diff --git a/source/lib/file.h b/source/lib/file.h
new file mode 100644 (file)
index 0000000..42d73b8
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef FILE_H_
+#define FILE_H_
+
+#include "filetarget.h"
+
+/**
+Just an arbitrary file.  No special meaning attached.
+*/
+class File: public FileTarget
+{
+public:
+       File(Builder &b, const Msp::FS::Path &t): FileTarget(b, t) { }
+       File(Builder &b, const SourcePackage &p, const Msp::FS::Path &t): FileTarget(b, p, t) { }
+
+       const char *get_type() const override { return "File"; }
+};
+
+#endif
diff --git a/source/lib/filetarget.cpp b/source/lib/filetarget.cpp
new file mode 100644 (file)
index 0000000..a2bd3f0
--- /dev/null
@@ -0,0 +1,169 @@
+#include <msp/core/algorithm.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include <msp/time/utils.h>
+#include "builder.h"
+#include "filetarget.h"
+#include "sourcepackage.h"
+#include "task.h"
+#include "tool.h"
+
+using namespace std;
+using namespace Msp;
+
+FileTarget::FileTarget(Builder &b, const SourcePackage *p, const FS::Path &a):
+       Target(b, generate_name(b, p, a)),
+       path(a)
+{
+       package = p;
+
+       builder.get_vfs().register_path(path, this);
+
+       stat();
+}
+
+void FileTarget::stat()
+{
+       if(FS::Stat st = FS::lstat(path))
+       {
+               mtime = st.get_modify_time();
+               size = st.get_size();
+       }
+}
+
+string FileTarget::generate_name(Builder &builder, const SourcePackage *pkg, const FS::Path &path)
+{
+       if(pkg && FS::descendant_depth(path, pkg->get_source_directory())>=0)
+       {
+               FS::Path relpath = FS::relative(path, pkg->get_source_directory());
+               return format("<%s>%s", pkg->get_name(), relpath.str().substr(1));
+       }
+       else if(FS::descendant_depth(path, builder.get_prefix())>=0)
+       {
+               FS::Path relpath = FS::relative(path, builder.get_prefix());
+               return "<prefix>"+relpath.str().substr(1);
+       }
+
+       return path.str();
+}
+
+void FileTarget::touch()
+{
+       mtime = Time::now();
+       modified();
+       signal_bubble_rebuild.emit();
+}
+
+void FileTarget::check_rebuild()
+{
+       if(!tool || needs_rebuild())
+               return;
+
+       if(!mtime)
+               mark_rebuild("Does not exist");
+       else
+       {
+               for(Target *d: depends)
+               {
+                       FileTarget *ft = dynamic_cast<FileTarget *>(d);
+                       if(ft && ft->get_mtime()>mtime)
+                               mark_rebuild(d->get_name()+" has changed");
+                       else if(d->needs_rebuild())
+                               mark_rebuild(d->get_name()+" needs rebuilding");
+                       if(needs_rebuild())
+                               break;
+               }
+       }
+
+       if(!needs_rebuild())
+       {
+               // Some side effects might not exist
+               auto i = find_if(side_effects, [](const Target *s){ return s->needs_rebuild(); });
+               if(i!=side_effects.end())
+                       mark_rebuild((*i)->get_name()+" needs rebuilding");
+       }
+
+       if(!needs_rebuild() && package)
+       {
+               if(package->get_config().get_mtime()>mtime)
+                       mark_rebuild("Package options changed");
+
+               if(tool->get_executable())
+               {
+                       string build_sig = create_build_signature();
+                       if(package->get_cache().has_key(this, "build_sig"))
+                       {
+                               if(package->get_cache().get_value(this, "build_sig")!=build_sig)
+                                       mark_rebuild("Build signature changed");
+                       }
+               }
+       }
+}
+
+string FileTarget::create_build_signature() const
+{
+       if(!package)
+               return string();
+
+       const BuildInfo &binfo = (component ? component->get_build_info() : package->get_build_info());
+       vector<string> sigs;
+
+       if(arch_in_build_sig)
+               if(const Architecture *arch = tool->get_architecture())
+                       sigs.push_back(arch->get_name());
+
+       sigs.push_back(tool->create_build_signature(binfo));
+
+       if(nested_build_sig && component)
+       {
+               vector<const Tool *> seen_tools;
+               vector<string> tool_sigs;
+               for(Target *d: depends)
+                       if(const Tool *t = d->get_tool())
+                               if(d->get_component()==component && !any_equals(seen_tools, t))
+                               {
+                                       seen_tools.push_back(t);
+                                       tool_sigs.push_back(t->create_build_signature(binfo));
+                               }
+
+               sort(tool_sigs);
+               sigs.insert(sigs.end(), make_move_iterator(tool_sigs.begin()), make_move_iterator(tool_sigs.end()));
+       }
+
+       return join(sigs.begin(), sigs.end(), ";");
+}
+
+void FileTarget::build(Task &task)
+{
+       task.add_file(path);
+       task.set_unlink(true);
+}
+
+void FileTarget::build_finished(bool success)
+{
+       if(success)
+       {
+               stat();
+               if(package)
+               {
+                       string build_sig = create_build_signature();
+                       if(!build_sig.empty())
+                               package->get_cache().set_value(this, "build_sig", build_sig);
+               }
+       }
+
+       Target::build_finished(success);
+}
+
+void FileTarget::clean()
+{
+       if(mtime)
+       {
+               FS::unlink(path);
+               mtime = Time::TimeStamp();
+               size = 0;
+               check_rebuild();
+       }
+}
diff --git a/source/lib/filetarget.h b/source/lib/filetarget.h
new file mode 100644 (file)
index 0000000..da165d0
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef FILETARGET_H_
+#define FILETARGET_H_
+
+#include <msp/fs/path.h>
+#include "target.h"
+
+/**
+An intermediate base class for targets that represent files.  Almost all target
+classes are derived from this.
+*/
+class FileTarget: public Target
+{
+protected:
+       Msp::FS::Path path;
+       Msp::Time::TimeStamp mtime;
+       unsigned size = 0;
+       Msp::FS::Path install_location;
+       std::string install_filename;
+       bool nested_build_sig = false;
+       bool arch_in_build_sig = false;
+
+       FileTarget(Builder &b, const Msp::FS::Path &a): FileTarget(b, 0, a) { }
+       FileTarget(Builder &b, const SourcePackage &p, const Msp::FS::Path &a): FileTarget(b, &p, a) { }
+private:
+       FileTarget(Builder &, const SourcePackage *, const Msp::FS::Path &);
+       void stat();
+       static std::string generate_name(Builder &, const SourcePackage *, const Msp::FS::Path &);
+
+public:
+       const Msp::FS::Path &get_path() const { return path; }
+       const Msp::Time::TimeStamp &get_mtime() const { return mtime; }
+       unsigned get_size() const { return size; }
+
+       bool is_installable() const { return !install_location.empty(); }
+       const Msp::FS::Path &get_install_location() const { return install_location; }
+       const std::string &get_install_filename() const { return install_filename; }
+
+       /// Changes the mtime of the target to the current time.
+       void touch();
+
+protected:
+       void check_rebuild() override;
+
+       virtual std::string create_build_signature() const;
+
+       void build(Task &) override;
+
+       void build_finished(bool) override;
+
+public:
+       void clean() override;
+};
+
+#endif
diff --git a/source/lib/importlibrary.cpp b/source/lib/importlibrary.cpp
new file mode 100644 (file)
index 0000000..435fb30
--- /dev/null
@@ -0,0 +1,35 @@
+#include <msp/strings/format.h>
+#include "architecture.h"
+#include "builder.h"
+#include "component.h"
+#include "exportdefinitions.h"
+#include "importlibrary.h"
+#include "sharedlibrary.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+ImportLibrary::ImportLibrary(Builder &b, const Component &c, SharedLibrary &sl, ExportDefinitions &exp):
+       FileTarget(b, c.get_package(), c.get_package().get_output_directory()/generate_filename(c, sl)),
+       shared_lib(&sl)
+{
+       component = &c;
+       add_dependency(exp);
+       shared_lib->set_import_library(this);
+
+       install_location = "lib";
+
+       const string &version = component->get_package().get_interface_version();
+       if(!version.empty())
+       {
+               const Architecture &arch = builder.get_current_arch();
+               install_filename = arch.create_filename<ImportLibrary>(format("%s-%s", sl.get_libname(), version));
+       }
+}
+
+string ImportLibrary::generate_filename(const Component &comp, const SharedLibrary &sl)
+{
+       const Architecture &arch = comp.get_package().get_builder().get_current_arch();
+       return arch.create_filename<ImportLibrary>(sl.get_libname());
+}
diff --git a/source/lib/importlibrary.h b/source/lib/importlibrary.h
new file mode 100644 (file)
index 0000000..1c5d682
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef IMPORTLIBRARY_H_
+#define IMPORTLIBRARY_H_
+
+#include "filetarget.h"
+
+class ExportDefinitions;
+class SharedLibrary;
+
+/**
+A special case of static library which pulls in a shared library.  Used on
+platforms with no true dynamic linking support.
+*/
+class ImportLibrary: public FileTarget
+{
+private:
+       SharedLibrary *shared_lib = 0;
+
+public:
+       ImportLibrary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { }
+       ImportLibrary(Builder &, const Component &, SharedLibrary &, ExportDefinitions &);
+private:
+       static std::string generate_filename(const Component &, const SharedLibrary &);
+
+public:
+       const char *get_type() const override { return "ImportLibrary"; }
+
+       SharedLibrary *get_shared_library() const { return shared_lib; }
+};
+
+#endif
diff --git a/source/lib/installcomponent.cpp b/source/lib/installcomponent.cpp
new file mode 100644 (file)
index 0000000..86919fa
--- /dev/null
@@ -0,0 +1,23 @@
+#include "installcomponent.h"
+#include "builder.h"
+#include "file.h"
+#include "sourcepackage.h"
+#include "tool.h"
+
+using namespace std;
+using namespace Msp;
+
+void InstallComponent::create_targets() const
+{
+       Builder &builder = package.get_builder();
+       Target *inst = builder.get_build_graph().get_target("install");
+       Tool &copy = builder.get_toolchain().get_tool("CP");
+
+       for(const FS::Path &s: collect_source_files())
+       {
+               Target *tgt = builder.get_vfs().get_target(s);
+               if(!tgt)
+                       tgt = new File(builder, package, s);
+               inst->add_dependency(*copy.create_target(*tgt, name));
+       }
+}
diff --git a/source/lib/installcomponent.h b/source/lib/installcomponent.h
new file mode 100644 (file)
index 0000000..457256d
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef INSTALLCOMPONENT_H_
+#define INSTALLCOMPONENT_H_
+
+#include "component.h"
+
+class InstallComponent: public Component
+{
+public:
+       InstallComponent(SourcePackage &p, const std::string &n): Component(p, n) { }
+
+       void create_targets() const override;
+};
+
+#endif
diff --git a/source/lib/installedfile.cpp b/source/lib/installedfile.cpp
new file mode 100644 (file)
index 0000000..6e45ad2
--- /dev/null
@@ -0,0 +1,92 @@
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include "builder.h"
+#include "installedfile.h"
+#include "sharedlibrary.h"
+
+using namespace std;
+using namespace Msp;
+
+InstalledFile::InstalledFile(Builder &b, const SourcePackage &p, FileTarget &s, const string &loc):
+       FileTarget(b, p, generate_target_path(b.get_prefix(), s, loc)),
+       source(s)
+{
+       add_dependency(source);
+}
+
+FS::Path InstalledFile::generate_target_path(const FS::Path &global_prefix, const FileTarget &tgt, const string &loc)
+{
+       if(!tgt.is_installable() && loc.empty())
+               throw invalid_argument(tgt.get_name()+" is not installable");
+
+       FS::Path prefix;
+       FS::Path mid;
+       if(!loc.compare(0, 2, "//"))
+       {
+               if(!tgt.get_package())
+                       throw invalid_argument("No private install location for "+tgt.get_name());
+
+               prefix = tgt.get_package()->get_temp_directory();
+               mid = loc.substr(2);
+       }
+       else
+       {
+               prefix = global_prefix;
+
+               if(!loc.empty())
+                       mid = loc;
+               else if(const Component *comp = tgt.get_component())
+                       mid = comp->get_install_map().get_install_location(tgt);
+       }
+
+       if(mid.empty())
+               mid = tgt.get_install_location();
+
+       string fn = tgt.get_install_filename();
+       if(fn.empty())
+               fn = FS::basename(tgt.get_path());
+
+       return prefix/mid/fn;
+}
+
+void InstalledFile::set_symlink(const FS::Path &l)
+{
+       FS::Path al = FS::dirname(path)/l;
+       if(al==path)
+               throw invalid_argument("InstalledFile::set_symlink");
+       link = FS::dirname(path)/l;
+       builder.get_vfs().register_path(link, this);
+}
+
+Target *InstalledFile::get_real_target()
+{
+       return source.get_real_target();
+}
+
+void InstalledFile::check_rebuild()
+{
+       if(!mtime)
+               mark_rebuild("Does not exist");
+       else if(source.get_mtime()>mtime || source.get_size()!=size)
+               mark_rebuild(source.get_name()+" has changed");
+       else if(source.needs_rebuild())
+               mark_rebuild(source.get_name()+" needs rebuilding");
+       if(!needs_rebuild() && !link.empty())
+       {
+               if(!FS::exists(link))
+                       mark_rebuild("Symlink does not exist");
+               else
+               {
+                       FS::Path rel_path = FS::relative(path, FS::dirname(link));
+                       if(FS::readlink(link)!=rel_path)
+                               mark_rebuild("Symlink needs updating");
+               }
+       }
+}
+
+void InstalledFile::clean()
+{
+       if(!link.empty() && mtime)
+               FS::unlink(link);
+       FileTarget::clean();
+}
diff --git a/source/lib/installedfile.h b/source/lib/installedfile.h
new file mode 100644 (file)
index 0000000..0dd671f
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef INSTALLEDFILE_H_
+#define INSTALLEDFILE_H_
+
+#include "sourcepackage.h"
+#include "filetarget.h"
+
+/**
+Represents the installation of a file.
+*/
+class InstalledFile: public FileTarget
+{
+private:
+       FileTarget &source;
+       Msp::FS::Path link;
+
+public:
+       InstalledFile(Builder &, const SourcePackage &, FileTarget &, const std::string & =std::string());
+private:
+       static Msp::FS::Path generate_target_path(const Msp::FS::Path &, const FileTarget &i, const std::string &);
+
+public:
+       const char *get_type() const override { return "InstalledFile"; }
+       FileTarget &get_source() const { return source; }
+
+       /** Sets a symlink for the file.  A relative path will be rooted at the
+       directory the file resides in. */
+       void set_symlink(const Msp::FS::Path &);
+
+       const Msp::FS::Path &get_symlink() const { return link; }
+
+       Target *get_real_target() override;
+private:
+       void check_rebuild() override;
+
+public:
+       void clean() override;
+};
+
+#endif
diff --git a/source/lib/installmap.cpp b/source/lib/installmap.cpp
new file mode 100644 (file)
index 0000000..448f997
--- /dev/null
@@ -0,0 +1,82 @@
+#include <msp/core/algorithm.h>
+#include <msp/fs/utils.h>
+#include "component.h"
+#include "filetarget.h"
+#include "installmap.h"
+#include "sourcepackage.h"
+#include "templatefile.h"
+
+using namespace std;
+using namespace Msp;
+
+void InstallMap::add_mapping(const FS::Path &src, const FS::Path &inst)
+{
+       Entry e;
+       e.source = src;
+       e.install = inst;
+       entries.push_back(e);
+}
+
+FS::Path InstallMap::get_install_location(const FileTarget &target) const
+{
+       const Component *comp = target.get_component();
+       unsigned overlay_depth = 0;
+       if(comp && !comp->get_overlays().empty())
+       {
+               // Check if the target resides in an overlay directory
+               string last_dir = FS::basename(FS::dirname(target.get_path()));
+               if(any_equals(comp->get_overlays(), last_dir))
+                       overlay_depth = 1;
+       }
+
+       FS::Path source = target.get_path();
+       if(comp)
+       {
+               /* Check if the target is a generated source file, residing in the
+               temporary directory */
+               const SourcePackage &pkg = comp->get_package();
+               int temp_depth = FS::descendant_depth(source, pkg.get_temp_directory());
+               if(temp_depth>0)
+               {
+                       // If it is, use the generating template's directory instead
+                       for(Target *d: target.get_dependencies())
+                               if(const TemplateFile *tmpl = dynamic_cast<const TemplateFile *>(d))
+                               {
+                                       source = FS::dirname(tmpl->get_path())/FS::basename(source);
+                                       break;
+                               }
+               }
+       }
+
+       /* Look for a mapping entry matching both the target's original location
+       and default install location */
+       FS::Path install = target.get_install_location();
+       for(const Entry &e: entries)
+       {
+               int source_depth = FS::descendant_depth(source, e.source);
+               if(source_depth>=0)
+               {
+                       FS::Path install_base = FS::common_ancestor(install, e.install);
+                       if(install_base.size()>1)
+                       {
+                               install = e.install/source.subpath(e.source.size(), source_depth-1-overlay_depth);
+                               break;
+                       }
+               }
+       }
+
+       return install;
+}
+
+
+InstallMap::Loader::Loader(InstallMap &m, const FS::Path &s):
+       DataFile::ObjectLoader<InstallMap>(m),
+       source_base(s)
+{
+       add("map", &Loader::map);
+}
+
+void InstallMap::Loader::map(const string &src, const string &inst)
+{
+       obj.add_mapping(source_base/src, inst);
+}
diff --git a/source/lib/installmap.h b/source/lib/installmap.h
new file mode 100644 (file)
index 0000000..49211d7
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef INSTALLMAP_H_
+#define INSTALLMAP_H_
+
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include <msp/fs/path.h>
+
+class FileTarget;
+
+/**
+Maps install locations based on location in the source tree.  Mappings are
+defined as pairs of source and install locations.  Targets within a source
+location are mapped if their default install location shares a common prefix
+with the mapped install location.  The remainder of the source location is
+appended to the mapped install location to form the final install location.
+*/
+class InstallMap
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<InstallMap>
+       {
+       private:
+               Msp::FS::Path source_base;
+
+       public:
+               Loader(InstallMap &, const Msp::FS::Path &);
+
+       private:
+               void map(const std::string &, const std::string &);
+       };
+
+private:
+       struct Entry
+       {
+               Msp::FS::Path source;
+               Msp::FS::Path install;
+       };
+
+       std::vector<Entry> entries;
+
+public:
+       /** Adds an install mapping.  Multiple mappings can be specified for the
+       same source location, but the first one that matches both that and the
+       target's default install location will be used. */
+       void add_mapping(const Msp::FS::Path &, const Msp::FS::Path &);
+
+       /** Returns the install location for a target.  If no defined mappings match
+       the target, its default install location is returned. */
+       Msp::FS::Path get_install_location(const FileTarget &) const;
+};
+
+#endif
diff --git a/source/lib/internaltask.cpp b/source/lib/internaltask.cpp
new file mode 100644 (file)
index 0000000..fc929ce
--- /dev/null
@@ -0,0 +1,36 @@
+#include "internaltask.h"
+
+InternalTask::~InternalTask()
+{
+       worker.join();
+}
+
+void InternalTask::start()
+{
+       prepare();
+       worker.launch();
+}
+
+Task::Status InternalTask::check()
+{
+       Status result = worker.get_status();
+       if(result!=RUNNING)
+               signal_finished.emit(result==SUCCESS);
+       return result;
+}
+
+Task::Status InternalTask::wait()
+{
+       Status result;
+       while((result = check())==RUNNING) ;
+       return result;
+}
+
+
+void InternalTask::Worker::main()
+{
+       if(func())
+               status = Task::SUCCESS;
+       else
+               status = Task::ERROR;
+}
diff --git a/source/lib/internaltask.h b/source/lib/internaltask.h
new file mode 100644 (file)
index 0000000..608ea96
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef INTERNALTASK_H_
+#define INTERNALTASK_H_
+
+#include <functional>
+#include <msp/core/thread.h>
+#include "task.h"
+
+/**
+Runs a worker thread.  Tools should derive a thread class from
+InternalTask::Worker.  The worker thread must set its status to either SUCCESS
+or ERROR before terminating.
+*/
+class InternalTask: public Task
+{
+private:
+       class Worker: public Msp::Thread
+       {
+               friend class InternalTask;
+
+       private:
+               std::function<bool()> func;
+               volatile Status status = Task::RUNNING;
+
+       public:
+               Worker(std::function<bool()> f): func(f) { }
+
+               Status get_status() const { return status; }
+
+       private:
+               void main() override;
+       };
+
+       Worker worker;
+
+public:
+       InternalTask(std::function<bool()> f): worker(f) { }
+       ~InternalTask();
+
+       std::string get_command() const override { return "<internal>"; }
+       void start() override;
+       Status check() override;
+       Status wait() override;
+};
+
+#endif
diff --git a/source/lib/logger.cpp b/source/lib/logger.cpp
new file mode 100644 (file)
index 0000000..35d4507
--- /dev/null
@@ -0,0 +1,37 @@
+#include <msp/core/algorithm.h>
+#include <msp/io/print.h>
+#include "logger.h"
+
+using namespace std;
+using namespace Msp;
+
+void Logger::enable_channel(const string &chan)
+{
+       auto i = lower_bound(enabled_channels, chan);
+       if(i==enabled_channels.end() || *i!=chan)
+               enabled_channels.insert(i, chan);
+}
+
+void Logger::disable_channel(const string &chan)
+{
+       auto i = lower_bound(enabled_channels, chan);
+       if(i!=enabled_channels.end() && *i==chan)
+               enabled_channels.erase(i);
+}
+
+bool Logger::is_channel_enabled(const string &chan) const
+{
+       auto i = lower_bound(enabled_channels, chan);
+       return (i!=enabled_channels.end() && *i==chan);
+}
+
+void Logger::log(const string &chan, const string &message) const
+{
+       if(is_channel_enabled(chan))
+               print(message);
+}
+
+void Logger::print(const string &message) const
+{
+       IO::print("%s\n", message);
+}
diff --git a/source/lib/logger.h b/source/lib/logger.h
new file mode 100644 (file)
index 0000000..3a10f0e
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef LOGGER_H_
+#define LOGGER_H_
+
+#include <string>
+#include <vector>
+#include <msp/strings/format.h>
+
+class Logger
+{
+private:
+       std::vector<std::string> enabled_channels;
+
+public:
+       void enable_channel(const std::string &);
+       void disable_channel(const std::string &);
+       bool is_channel_enabled(const std::string &) const;
+
+       void log(const std::string &, const std::string &) const;
+
+       template<typename... Args>
+       void log(const std::string &, const std::string &, Args &&...) const;
+
+private:
+       void print(const std::string &) const;
+};
+
+template<typename... Args>
+void Logger::log(const std::string &chan, const std::string &fmt, Args &&... args) const
+{
+       if(is_channel_enabled(chan))
+               print(Msp::format(fmt, std::forward<Args>(args)...));
+}
+
+#endif
diff --git a/source/lib/objcsourcefile.cpp b/source/lib/objcsourcefile.cpp
new file mode 100644 (file)
index 0000000..953819f
--- /dev/null
@@ -0,0 +1,15 @@
+#include <msp/strings/regex.h>
+#include "objcsourcefile.h"
+
+using namespace std;
+using namespace Msp;
+
+void ObjCSourceFile::parse_includes(IO::Base &in)
+{
+       static Regex r_include("^[ \t]*#(include|import)[ \t]+([\"<].*)[\">]");
+
+       string line;
+       while(in.getline(line))
+               if(RegMatch match = r_include.match(line))
+                       includes.push_back(match[2].str);
+}
diff --git a/source/lib/objcsourcefile.h b/source/lib/objcsourcefile.h
new file mode 100644 (file)
index 0000000..37f79a3
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef OBJCSOURCEFILE_H_
+#define OBJCSOURCEFILE_H_
+
+#include "csourcefile.h"
+
+/**
+Represents an Objective-C source file.
+*/
+class ObjCSourceFile: public CSourceFile
+{
+public:
+       using CSourceFile::CSourceFile;
+
+       const char *get_type() const override { return "ObjCSourceFile"; }
+
+protected:
+       void parse_includes(Msp::IO::Base &) override;
+};
+
+#endif
diff --git a/source/lib/objectfile.cpp b/source/lib/objectfile.cpp
new file mode 100644 (file)
index 0000000..00fa679
--- /dev/null
@@ -0,0 +1,110 @@
+#include <msp/core/algorithm.h>
+#include <msp/fs/utils.h>
+#include "builder.h"
+#include "component.h"
+#include "objectfile.h"
+#include "sourcefile.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+ObjectFile::ObjectFile(Builder &b, const Component &c, SourceFile &s):
+       FileTarget(b, c.get_package(), generate_target_path(c, s.get_path())),
+       source(s)
+{
+       component = &c;
+       add_dependency(source);
+}
+
+FS::Path ObjectFile::generate_target_path(const Component &comp, const FS::Path &src)
+{
+       const SourcePackage &pkg = comp.get_package();
+       FS::Path temp_dir = pkg.get_temp_directory();
+       FS::Path rel_src;
+       if(FS::descendant_depth(src, temp_dir)>=0)
+               rel_src = FS::relative(src, temp_dir);
+       else
+               rel_src = FS::relative(src, pkg.get_source_directory());
+       string fn;
+       for(const string &c: rel_src)
+       {
+               if(!fn.empty())
+                       fn += '_';
+               if(c!=".")
+                       fn += c;
+       }
+       const Architecture &arch = comp.get_package().get_builder().get_current_arch();
+       return temp_dir/comp.get_name()/arch.create_filename<ObjectFile>(FS::basepart(fn));
+}
+
+void ObjectFile::set_used_in_shared_library(bool u)
+{
+       used_in_shlib = u;
+}
+
+void ObjectFile::collect_build_info(BuildInfo &binfo) const
+{
+       Target::collect_build_info(binfo);
+       binfo.update_from(component->get_build_info_for_path(source.get_path()));
+}
+
+void ObjectFile::find_dependencies()
+{
+       vector<FileTarget *> headers;
+       find_dependencies(source, headers);
+       for(FileTarget *h: headers)
+       {
+               add_dependency(*h);
+               if(h->get_real_target()->is_buildable())
+                       h->signal_modified.connect(sigc::mem_fun(this, static_cast<void (ObjectFile::*)()>(&ObjectFile::find_dependencies)));
+       }
+}
+
+void ObjectFile::find_dependencies(FileTarget &tgt, vector<FileTarget *> &headers)
+{
+       tgt.prepare();
+
+       FileTarget *rtgt = dynamic_cast<FileTarget *>(tgt.get_real_target());
+       Dependencies deps_to_add = rtgt->get_transitive_dependencies();
+       if(rtgt!=&tgt)
+       {
+               FS::Path inst_dir = rtgt->get_component()->get_install_map().get_install_location(*rtgt);
+               /* The target has been displaced by installing it.  Displace any
+               dependencies that come from the same package as well. */
+               const SourcePackage *tpkg = rtgt->get_package();
+               for(Target *&d: deps_to_add)
+               {
+                       FileTarget *file = dynamic_cast<FileTarget *>(d);
+                       if(file && file->get_package()==tpkg && FS::descendant_depth(file->get_path(), tpkg->get_source_directory())>=0)
+                       {
+                               const Component *tcomp = file->get_component();
+                               FS::Path dep_inst = tcomp->get_install_map().get_install_location(*file);
+                               FS::Path displaced = FS::dirname(tgt.get_path())/FS::relative(dep_inst, inst_dir)/FS::basename(file->get_path());
+                               d = builder.get_vfs().get_target(displaced);
+                               if(!d)
+                               {
+                                       /* If the target was in an overlay directory and the displaced
+                                       dependency is not found, try removing the overlay from the path. */
+                                       string last_dir = FS::basename(FS::dirname(displaced));
+                                       if(any_equals(tcomp->get_overlays(), last_dir))
+                                       {
+                                               displaced = displaced.subpath(0, displaced.size()-2)/FS::basename(file->get_path());
+                                               d = builder.get_vfs().get_target(displaced);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       for(Target *d: deps_to_add)
+               if(FileTarget *file = dynamic_cast<FileTarget *>(d))
+               {
+                       auto i = lower_bound(headers, file);
+                       if(i==headers.end() || *i!=file)
+                       {
+                               headers.insert(i, file);
+                               find_dependencies(*file, headers);
+                       }
+               }
+}
diff --git a/source/lib/objectfile.h b/source/lib/objectfile.h
new file mode 100644 (file)
index 0000000..9e3ff90
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef OBJECTFILE_H_
+#define OBJECTFILE_H_
+
+#include "filetarget.h"
+
+class SourceFile;
+
+/**
+Object files are compiled from source files.
+*/
+class ObjectFile: public FileTarget
+{
+private:
+       SourceFile &source;
+       bool used_in_shlib = false;
+
+public:
+       ObjectFile(Builder &, const Component &, SourceFile &);
+private:
+       static Msp::FS::Path generate_target_path(const Component &, const Msp::FS::Path &);
+
+public:
+       const char *get_type() const override { return "ObjectFile"; }
+       SourceFile &get_source() const { return source; }
+
+       void set_used_in_shared_library(bool);
+       bool is_used_in_shared_library() const { return used_in_shlib; }
+
+       void collect_build_info(BuildInfo &) const override;
+
+private:
+       void find_dependencies() override;
+
+       void find_dependencies(FileTarget &, std::vector<FileTarget *> &);
+};
+
+#endif
diff --git a/source/lib/package.cpp b/source/lib/package.cpp
new file mode 100644 (file)
index 0000000..10c2db0
--- /dev/null
@@ -0,0 +1,44 @@
+#include <msp/strings/format.h>
+#include "builder.h"
+#include "package.h"
+
+using namespace std;
+using namespace Msp;
+
+Package::Package(Builder &b, const string &n):
+       builder(b),
+       name(n),
+       label(string(1, toupper(n[0]))+n.substr(1))
+{
+       builder.get_package_manager().add_package(this);
+}
+
+void Package::prepare()
+{
+       if(prepared)
+               return;
+
+       for(Package *r: requires)
+               r->prepare();
+
+       do_prepare();
+       prepared = true;
+}
+
+
+Package::Loader::Loader(Package &p):
+       DataFile::ObjectLoader<Package>(p),
+       ArchitectureConditional(p.builder, p.name)
+{
+       add("label",   &Package::label);
+       add("require", &Loader::require);
+}
+
+void Package::Loader::require(const string &n)
+{
+       Package *req = obj.builder.get_package_manager().find_package(n);
+       if(req)
+               obj.requires.push_back(req);
+       else
+               obj.problems.push_back(format("Required package %s not found", n));
+}
diff --git a/source/lib/package.h b/source/lib/package.h
new file mode 100644 (file)
index 0000000..d13aa73
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef PACKAGE_H_
+#define PACKAGE_H_
+
+#include <string>
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include "buildinfo.h"
+#include "conditionalloader.h"
+#include "config.h"
+
+class Builder;
+class Package;
+
+/**
+A package is a distributable piece of software.  Package information may be
+obtained in several ways: Build files of source packages, pkg-config for binary
+packages and the builderrc file for binary packages with no pkg-config support.
+*/
+class Package
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<Package>, public ArchitectureConditional
+       {
+       public:
+               Loader(Package &);
+       private:
+               void require(const std::string &);
+       };
+
+       using Requirements = std::vector<Package *>;
+
+protected:
+       Builder &builder;
+
+       std::string name;
+       std::string label;
+
+       Requirements requires;
+       BuildInfo export_binfo;
+       bool prepared = false;
+       std::vector<std::string> problems;
+
+       bool use_pkgconfig = true;
+
+       Package(Builder &, const std::string &);
+public:
+       virtual ~Package() { }
+
+       Builder &get_builder() const { return builder; }
+       const std::string &get_name() const { return name; }
+       const std::string &get_label() const { return label; }
+       const Requirements &get_required_packages() const { return requires; }
+
+       const BuildInfo &get_exported_build_info() const { return export_binfo; }
+
+       /// Indicates whether or not this package supports pkg-config
+       bool uses_pkgconfig() const { return use_pkgconfig; }
+
+       /** Prepares the package for building.  Recursively prepares all required
+       packages, populates build info and creates targets. */
+       void prepare();
+
+protected:
+       virtual void do_prepare() { }
+
+public:
+       bool is_prepared() const { return prepared; }
+
+       const std::vector<std::string> &get_problems() const { return problems; }
+
+       virtual void save_caches() { }
+};
+
+#endif
diff --git a/source/lib/packagemanager.cpp b/source/lib/packagemanager.cpp
new file mode 100644 (file)
index 0000000..37374b6
--- /dev/null
@@ -0,0 +1,247 @@
+#include <cstdlib>
+#include <msp/core/algorithm.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/io/print.h>
+#include <msp/strings/utils.h>
+#include <msp/time/timedelta.h>
+#include <msp/time/utils.h>
+#include "binarypackage.h"
+#include "builder.h"
+#include "externaltask.h"
+#include "package.h"
+#include "packagemanager.h"
+
+using namespace std;
+using namespace Msp;
+
+PackageManager::~PackageManager()
+{
+       for(const auto &kvp: packages)
+               delete kvp.second;
+}
+
+void PackageManager::append_package_path(const FS::Path &p)
+{
+       pkg_path.push_back(p);
+}
+
+void PackageManager::append_binary_package_path(const FS::Path &p)
+{
+       binpkg_path.push_back(p);
+}
+
+void PackageManager::set_no_externals(bool x)
+{
+       no_externals = x;
+}
+
+void PackageManager::add_package(Package *pkg)
+{
+       auto i = packages.find(pkg->get_name());
+       if(i!=packages.end())
+       {
+               if(i->second!=pkg)
+                       throw invalid_argument("Package name conflict");
+               else
+                       throw logic_error("Package is already managed");
+       }
+
+       if(packages.empty())
+               main_pkg = pkg;
+
+       packages.insert({ pkg->get_name(), pkg });
+}
+
+Package *PackageManager::get_package(const string &name) const
+{
+       auto i = packages.find(name);
+       if(i!=packages.end())
+               return i->second;
+
+       return 0;
+}
+
+Package &PackageManager::get_main_package() const
+{
+       if(!main_pkg)
+               throw logic_error("No packages");
+       return *main_pkg;
+}
+
+Package *PackageManager::find_package(const string &name)
+{
+       if(not_found.count(name))
+               return 0;
+
+       if(Package *pkg = get_package(name))
+               return pkg;
+
+       if(!no_externals)
+       {
+               FS::Path path = get_package_location(name);
+               if(!path.empty())
+               {
+                       builder.load_build_file(path/"Build");
+                       auto i = packages.find(name);
+                       if(i!=packages.end())
+                               return i->second;
+               }
+       }
+
+       FS::Path path = get_binary_package_file(name);
+       if(!path.empty())
+       {
+               builder.load_build_file(path);
+               if(Package *pkg = get_package(name))
+                       return pkg;
+       }
+
+       try
+       {
+               // Package source not found - create a binary package
+               string flags_str = run_pkgconfig(name, "flags");
+               BinaryPackage::Flags flags = split(flags_str);
+               flags_str = run_pkgconfig(name, "staticflags");
+               BinaryPackage::Flags static_flags = split(flags_str);
+               Package *pkg = BinaryPackage::from_flags(builder, name, flags, static_flags);
+               packages.insert({ name, pkg });
+               return pkg;
+       }
+       catch(...)
+       {
+               not_found.insert(name);
+               return 0;
+       }
+}
+
+string PackageManager::run_pkgconfig(const string &pkg, const string &what)
+{
+#ifndef _WIN32
+       if(!env_set)
+       {
+               const FS::Path &prefix = builder.get_prefix();
+               if(prefix.str()!="/usr")
+               {
+                       FS::Path pcdir = prefix/"lib/pkgconfig";
+                       if(const char *pcp = getenv("PKG_CONFIG_PATH"))
+                       {
+                               vector<string> path = split(pcp, ':');
+                               if(!any_equals(path, pcdir.str()))
+                               {
+                                       path.push_back(pcdir.str());
+                                       setenv("PKG_CONFIG_PATH", join(path.begin(), path.end(), ":").c_str(), true);
+                               }
+                       }
+                       else
+                               setenv("PKG_CONFIG_PATH", pcdir.str().c_str(), true);
+               }
+       }
+
+       ExternalTask::Arguments argv;
+       argv.push_back("pkg-config");
+       if(what=="cflags" || what=="libs")
+               argv.push_back("--"+what);
+       else if(what=="flags" || what=="staticflags")
+       {
+               argv.push_back("--cflags");
+               argv.push_back("--libs");
+               if(what=="staticflags")
+                       argv.push_back("--static");
+       }
+       else
+               argv.push_back("--variable="+what);
+       argv.push_back(pkg);
+
+       builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
+
+       return ExternalTask::run_and_capture_output(argv);
+#else
+       (void)pkg;
+       (void)what;
+       return string();
+#endif
+}
+
+FS::Path PackageManager::get_package_location(const string &name)
+{
+       builder.get_logger().log("packagemgr", "Looking for source package %s", name);
+
+       try
+       {
+               // Try to get source directory with pkgconfig
+               string srcdir = strip(run_pkgconfig(name, "source"));
+               if(!srcdir.empty() && FS::exists(FS::Path(srcdir)/"Build"))
+                       return srcdir;
+       }
+       catch(...)
+       { }
+
+       if(pkg_dirs.empty())
+       {
+               for(const FS::Path &p: pkg_path)
+               {
+                       builder.get_logger().log("files", "Traversing %s", p);
+                       unsigned count = 0;
+                       for(const string &f: list_files(p))
+                       {
+                               FS::Path full = p/f;
+                               if(FS::exists(full/"Build"))
+                               {
+                                       pkg_dirs.push_back(full);
+                                       ++count;
+                               }
+                       }
+
+                       builder.get_logger().log("packagemgr", "%d source packages found in %s", count, p);
+               }
+
+               builder.get_logger().log("packagemgr", "%d source packages found", pkg_dirs.size());
+       }
+
+       bool msp = !name.compare(0, 3, "msp");
+       for(const FS::Path &p: pkg_dirs)
+       {
+               string base = FS::basename(p);
+               unsigned dash = base.rfind('-');
+
+               if(!base.compare(0, dash, name))
+                       return p;
+               else if(msp && !base.compare(0, dash, name, 3, string::npos))
+                       return p;
+       }
+
+       return FS::Path();
+}
+
+FS::Path PackageManager::get_binary_package_file(const string &name)
+{
+       builder.get_logger().log("packagemgr", "Looking for binary package %s", name);
+
+       if(binpkg_files.empty())
+       {
+               for(const FS::Path &p: binpkg_path)
+               {
+                       builder.get_logger().log("files", "Traversing %s", p);
+                       vector<string> files = list_filtered(p, "\\.bpk$");
+                       for(const string &f: files)
+                               binpkg_files.push_back(p/f);
+                       builder.get_logger().log("packagemgr", "%d binary packages found in %s", files.size(), p);
+               }
+
+               builder.get_logger().log("packagemgr", "%d binary packages found", binpkg_files.size());
+       }
+
+       auto i = find_if(binpkg_files, [&name](const FS::Path &p){ return FS::basepart(FS::basename(p))==name; });
+       if(i!=binpkg_files.end())
+               return *i;
+
+       return FS::Path();
+}
+
+void PackageManager::save_all_caches() const
+{
+       for(const auto &kvp: packages)
+               kvp.second->save_caches();
+}
diff --git a/source/lib/packagemanager.h b/source/lib/packagemanager.h
new file mode 100644 (file)
index 0000000..fc0aa98
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef PACKAGEMANAGER_H_
+#define PACKAGEMANAGER_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <msp/fs/path.h>
+
+class Builder;
+class Package;
+
+/**
+Keeps track of packages.  Also responsible for locating previously unknown
+packages by name.
+*/
+class PackageManager
+{
+private:
+       Builder &builder;
+       std::vector<Msp::FS::Path> pkg_path;
+       std::vector<Msp::FS::Path> pkg_dirs;
+       std::vector<Msp::FS::Path> binpkg_path;
+       std::vector<Msp::FS::Path> binpkg_files;
+       bool no_externals = false;
+       std::map<std::string, Package *> packages;
+       Package *main_pkg = 0;
+       std::set<std::string> not_found;
+       bool env_set = false;
+
+public:
+       PackageManager(Builder &b): builder(b) { }
+       ~PackageManager();
+
+       /// Adds a location to look for source packages from.
+       void append_package_path(const Msp::FS::Path &);
+
+       /// Adds a location to look for binary packages from.
+       void append_binary_package_path(const Msp::FS::Path &);
+
+       /** Prevent creation of source packages. */
+       void set_no_externals(bool);
+
+       /** Adds a package to the manager.  Called from Package constructor. */
+       void add_package(Package *);
+
+       /** Returns a package from the cache. */
+       Package *get_package(const std::string &) const;
+
+       /** Returns the package that was added first.  This should be considered
+       the primary build target. */
+       Package &get_main_package() const;
+
+       const std::map<std::string, Package *> &get_packages() const { return packages; }
+
+       /** Locates a package and loads it if necessary. */
+       Package *find_package(const std::string &);
+
+private:
+       std::string run_pkgconfig(const std::string &, const std::string &);
+
+       /** Determines the source directory of a package.  Pkg-config is consulted
+       first, and if it fails, the package path is searched for matches.  The
+       package is expected to be located in a directory named after itself. */
+       Msp::FS::Path get_package_location(const std::string &);
+
+       /** Determines the file containing a binary package.  The file is expected
+       to be named after the package. */
+       Msp::FS::Path get_binary_package_file(const std::string &);
+
+public:
+       void save_all_caches() const;
+};
+
+#endif
diff --git a/source/lib/pattern.cpp b/source/lib/pattern.cpp
new file mode 100644 (file)
index 0000000..fec83da
--- /dev/null
@@ -0,0 +1,31 @@
+#include <stdexcept>
+#include "pattern.h"
+
+using namespace std;
+
+Pattern::Pattern(const string &pat)
+{
+       string::size_type percent = pat.find('%');
+       if(percent==string::npos)
+               throw invalid_argument("No percent sign in pattern");
+       prefix = pat.substr(0, percent);
+       suffix = pat.substr(percent+1);
+}
+
+string Pattern::apply(const string &body) const
+{
+       string result = body;
+       if(body.compare(0, prefix.size(), prefix))
+               result = prefix+result;
+       if(body.size()<=suffix.size() || body.compare(body.size()-suffix.size(), suffix.size(), suffix))
+               result += suffix;
+       return result;
+}
+
+vector<string> Pattern::apply_list(const vector<Pattern> &patterns, const string &body)
+{
+       vector<string> result;
+       for(const Pattern &p: patterns)
+               result.push_back(p.apply(body));
+       return result;
+}
diff --git a/source/lib/pattern.h b/source/lib/pattern.h
new file mode 100644 (file)
index 0000000..8c623bd
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef PATTERN_H_
+#define PATTERN_H_
+
+#include <string>
+#include <vector>
+
+/**
+Stores a filename pattern.  A pattern consists of a prefix and a suffix, and
+can be applied to a body to form a complete filename.  Either or both of the
+prefix and suffix may be empty.
+*/
+class Pattern
+{
+private:
+       std::string prefix;
+       std::string suffix;
+
+public:
+       /** Constructs a pattern from a single string.  The string must have exactly
+       one percent sign (%) to separate the prefix and suffix. */
+       Pattern(const std::string &);
+
+       /** Applies the pattern to a body string. */
+       std::string apply(const std::string &) const;
+
+       /** Applies a list of patterns to the same body. */
+       static std::vector<std::string> apply_list(const std::vector<Pattern> &, const std::string &);
+};
+
+#endif
diff --git a/source/lib/sharedlibrary.cpp b/source/lib/sharedlibrary.cpp
new file mode 100644 (file)
index 0000000..0673a05
--- /dev/null
@@ -0,0 +1,69 @@
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include "binarycomponent.h"
+#include "builder.h"
+#include "objectfile.h"
+#include "sharedlibrary.h"
+#include "sourcepackage.h"
+
+using namespace std;
+using namespace Msp;
+
+SharedLibrary::SharedLibrary(Builder &b, const Msp::FS::Path &p):
+       Binary(b, p)
+{
+       libname = FS::basepart(FS::basename(path));
+       if(!libname.compare(0, 3, "lib"))
+               libname = libname.substr(3);
+}
+
+SharedLibrary::SharedLibrary(Builder &b, const Component &c, const vector<ObjectFile *> &objs):
+       Binary(b, c, generate_filename(c), objs),
+       libname(c.get_name()),
+       import_lib(0)
+{
+       if(builder.get_current_arch().get_system()=="windows")
+               install_location = "bin";
+       else
+               install_location = "lib";
+
+       const BinaryComponent &bcomp = dynamic_cast<const BinaryComponent &>(*component);
+       if(bcomp.get_type()==BinaryComponent::MODULE)
+               install_location /= package->get_name();
+       else
+       {
+               const string &version = component->get_package().get_interface_version();
+               if(!version.empty())
+               {
+                       const Architecture &arch = builder.get_current_arch();
+                       if(arch.get_system()=="windows")
+                               soname = arch.create_filename<SharedLibrary>(format("%s-%s", libname, version));
+                       else if(arch.get_system()=="darwin")
+                               soname = arch.create_filename<SharedLibrary>(format("%s.%s", libname, version));
+                       else
+                               soname = format("%s.%s", arch.create_filename<SharedLibrary>(libname), version);
+
+                       install_filename = soname;
+               }
+       }
+
+       for(ObjectFile *o: objects)
+               o->set_used_in_shared_library(true);
+}
+
+string SharedLibrary::generate_filename(const Component &comp)
+{
+       const BinaryComponent &bcomp = dynamic_cast<const BinaryComponent &>(comp);
+       if(bcomp.get_type()==BinaryComponent::MODULE)
+               return comp.get_name()+".dlm";
+       else
+       {
+               const Architecture &arch = comp.get_package().get_builder().get_current_arch();
+               return arch.create_filename<SharedLibrary>(comp.get_name());
+       }
+}
+
+void SharedLibrary::set_import_library(ImportLibrary *imp)
+{
+       import_lib = imp;
+}
diff --git a/source/lib/sharedlibrary.h b/source/lib/sharedlibrary.h
new file mode 100644 (file)
index 0000000..564b210
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef SHAREDLIB_H_
+#define SHAREDLIB_H_
+
+#include "binary.h"
+
+class ImportLibrary;
+
+/**
+Represents a shared library.  It has two special properties: libname and
+soname.  Libname is the name used by the linker.  Soname is the canonical
+filename of the library, including version number.  If the owning package has
+no version, soname will be empty.
+
+A SharedLibrary can also store a pointer to the associated ImportLibrary, for
+platforms that need one.
+*/
+class SharedLibrary: public Binary
+{
+private:
+       std::string libname;
+       std::string soname;
+       ImportLibrary *import_lib = 0;
+
+public:
+       SharedLibrary(Builder &, const Msp::FS::Path &);
+       SharedLibrary(Builder &, const Component &, const std::vector<ObjectFile *> &);
+private:
+       static std::string generate_filename(const Component &);
+
+public:
+       const char *get_type() const override { return "SharedLibrary"; }
+       const std::string &get_libname() const { return libname; }
+       const std::string &get_soname() const { return soname; }
+
+       void set_import_library(ImportLibrary *);
+       ImportLibrary *get_import_library() const { return import_lib; }
+};
+
+#endif
diff --git a/source/lib/sourcearchivecomponent.cpp b/source/lib/sourcearchivecomponent.cpp
new file mode 100644 (file)
index 0000000..b1e4279
--- /dev/null
@@ -0,0 +1,43 @@
+#include <msp/core/algorithm.h>
+#include "builder.h"
+#include "file.h"
+#include "sourcearchivecomponent.h"
+#include "sourcepackage.h"
+#include "tool.h"
+
+using namespace std;
+using namespace Msp;
+
+SourceArchiveComponent::SourceArchiveComponent(SourcePackage &p):
+       Component(p, p.get_name()+"-source")
+{ }
+
+void SourceArchiveComponent::create_targets() const
+{
+       Builder &builder = package.get_builder();
+
+       vector<Target *> files;
+       files.push_back(&package.get_build_file());
+
+       for(const FS::Path &s: collect_source_files())
+       {
+               FileTarget *file = builder.get_vfs().get_target(s);
+               if(!file)
+                       file = new File(builder, package, s);
+               files.push_back(file);
+       }
+
+       BuildGraph &build_graph = builder.get_build_graph();
+       for(const auto &kvp: build_graph.get_targets())
+               if(kvp.second->get_package()==&package && !kvp.second->is_buildable())
+                       if(!any_equals(files, kvp.second))
+                               files.push_back(kvp.second);
+
+       const Toolchain &toolchain = builder.get_toolchain();
+       string archive_name = package.get_name();
+       if(!package.get_version().empty())
+               archive_name += "-"+package.get_version();
+       archive_name += "-source";
+       Target *result = toolchain.get_tool("TAR").create_target(files, archive_name);
+       build_graph.get_target("archives")->add_dependency(*result);
+}
diff --git a/source/lib/sourcearchivecomponent.h b/source/lib/sourcearchivecomponent.h
new file mode 100644 (file)
index 0000000..94c1bc5
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef TARBALLCOMPONENT_H_
+#define TARBALLCOMPONENT_H_
+
+#include "component.h"
+
+class SourceArchiveComponent: public Component
+{
+public:
+       SourceArchiveComponent(SourcePackage &);
+
+       void create_targets() const override;
+};
+
+#endif
diff --git a/source/lib/sourcefile.cpp b/source/lib/sourcefile.cpp
new file mode 100644 (file)
index 0000000..b5b84c7
--- /dev/null
@@ -0,0 +1,9 @@
+#include "component.h"
+#include "sourcefile.h"
+#include "sourcepackage.h"
+
+SourceFile::SourceFile(Builder &b, const Component &c, const Msp::FS::Path &p):
+       FileTarget(b, c.get_package(), p)
+{
+       component = &c;
+}
diff --git a/source/lib/sourcefile.h b/source/lib/sourcefile.h
new file mode 100644 (file)
index 0000000..16521a5
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef SOURCEFILE_H_
+#define SOURCEFILE_H_
+
+#include "filetarget.h"
+
+class SourceFile: public FileTarget
+{
+protected:
+       SourceFile(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { }
+       SourceFile(Builder &, const Component &, const Msp::FS::Path &);
+};
+
+#endif
diff --git a/source/lib/sourcegenerator.cpp b/source/lib/sourcegenerator.cpp
new file mode 100644 (file)
index 0000000..d30a2bc
--- /dev/null
@@ -0,0 +1,132 @@
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include "builder.h"
+#include "executable.h"
+#include "sourcegenerator.h"
+#include "sourcepackage.h"
+#include "templatefile.h"
+
+using namespace std;
+using namespace Msp;
+
+SourceGenerator::SourceGenerator(Builder &b, const SourcePackage &p, const string &t):
+       Tool(b, t),
+       package(p)
+{
+       set_run_external(&_run);
+}
+
+Target *SourceGenerator::create_source(const Component &comp, const FS::Path &path) const
+{
+       return new TemplateFile(builder, comp, path);
+}
+
+Target *SourceGenerator::create_target(const vector<Target *> &sources, const string &)
+{
+       if(sources.empty())
+               throw invalid_argument("SourceGenerator::create_target");
+       if(out_suffixes.empty())
+               throw logic_error("No output suffixes");
+
+       TemplateFile &tmpl = dynamic_cast<TemplateFile &>(*sources.front());
+       const Component *comp = tmpl.get_component();
+       const SourcePackage *pkg = tmpl.get_package();
+       FS::Path subdir;
+       string base;
+       if(processing_unit==COMPONENT)
+               base = comp->get_name();
+       else
+       {
+               subdir = FS::dirname(FS::relative(tmpl.get_path(), pkg->get_source_directory()));
+               if(processing_unit==ONE_FILE)
+                       base = FS::basepart(FS::basename(tmpl.get_path()));
+               else if(processing_unit==DIRECTORY)
+               {
+                       base = FS::basename(subdir);
+                       subdir = FS::dirname(subdir);
+               }
+       }
+
+       Target *primary = 0;
+       for(const string &s: out_suffixes)
+       {
+               Tool *tool = builder.get_toolchain().get_tool_for_suffix(s, true);
+               if(tool)
+               {
+                       FS::Path fn = pkg->get_temp_directory()/"generated"/subdir/(base+s);
+                       Target *target = tool->create_source(*comp, fn);
+                       target->set_tool(*this);
+                       for(Target *t: sources)
+                               target->add_dependency(*t);
+                       if(primary)
+                               primary->add_side_effect(*target);
+                       else
+                               primary = target;
+               }
+               else
+                       throw runtime_error("No tool found for suffix "+s);
+       }
+
+       return primary;
+}
+
+ExternalTask::Arguments SourceGenerator::_run(const SourceFile &out_src, FS::Path &work_dir)
+{
+       const SourceGenerator &tool = dynamic_cast<const SourceGenerator &>(*out_src.get_tool());
+
+       vector<string> args;
+       args.push_back(tool.get_executable()->get_path().str());
+       args.insert(args.end(), tool.arguments.begin(), tool.arguments.end());
+
+       for(const Target *d: out_src.get_dependencies())
+               if(const TemplateFile *tmpl = dynamic_cast<const TemplateFile *>(d))
+                       args.push_back(FS::relative(tmpl->get_path(), work_dir).str());
+
+       if(!tool.out_argument.empty())
+               args.push_back(tool.out_argument);
+       args.push_back(FS::relative(out_src.get_path(), work_dir).str());
+
+       return args;
+}
+
+
+SourceGenerator::Loader::Loader(SourceGenerator &sg):
+       DataFile::ObjectLoader<SourceGenerator>(sg),
+       ConditionalLoader(sg.package, format("%s/%s", sg.package.get_name(), sg.tag))
+{
+       add("argument",   &Loader::argument);
+       add("arguments",  &Loader::arguments);
+       add("command",    &Loader::command);
+       add("in_suffix",  &Loader::in_suffix);
+       add("out_argument", &SourceGenerator::out_argument);
+       add("out_suffix", &Loader::out_suffix);
+       add("processing_unit", static_cast<ProcessingUnit SourceGenerator::*>(&SourceGenerator::processing_unit));
+}
+
+void SourceGenerator::Loader::argument(const string &a)
+{
+       obj.arguments.push_back(a);
+}
+
+void SourceGenerator::Loader::arguments(const vector<string> &a)
+{
+       obj.arguments.insert(obj.arguments.end(), a.begin(), a.end());
+}
+
+void SourceGenerator::Loader::command(const string &c)
+{
+       if(c.find('/')!=string::npos)
+               obj.set_command((obj.package.get_source_directory()/c).str());
+       else
+               obj.set_command(c);
+}
+
+void SourceGenerator::Loader::in_suffix(const string &s)
+{
+       obj.input_suffixes.push_back(s);
+}
+
+void SourceGenerator::Loader::out_suffix(const string &s)
+{
+       obj.out_suffixes.push_back(s);
+}
diff --git a/source/lib/sourcegenerator.h b/source/lib/sourcegenerator.h
new file mode 100644 (file)
index 0000000..b8dee24
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef SOURCEGENERATOR_H_
+#define SOURCEGENERATOR_H_
+
+#include <msp/datafile/objectloader.h>
+#include "conditionalloader.h"
+#include "sourcepackage.h"
+#include "tool.h"
+
+class SourceFile;
+
+class SourceGenerator: public Tool
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<SourceGenerator>, public ConditionalLoader
+       {
+       public:
+               Loader(SourceGenerator &);
+
+       private:
+               void argument(const std::string &);
+               void arguments(const std::vector<std::string> &);
+               void command(const std::string &);
+               void in_suffix(const std::string &);
+               void out_argument(const std::string &);
+               void out_suffix(const std::string &);
+       };
+
+private:
+       const SourcePackage &package;
+       std::vector<std::string> out_suffixes;
+       std::vector<std::string> arguments;
+       std::string out_argument;
+
+public:
+       SourceGenerator(Builder &, const SourcePackage &, const std::string &);
+
+       Target *create_source(const Component &, const Msp::FS::Path &) const override;
+       Target *create_target(const std::vector<Target *> &, const std::string &) override;
+
+private:
+       static ExternalTask::Arguments _run(const SourceFile &, Msp::FS::Path &);
+};
+
+#endif
diff --git a/source/lib/sourcepackage.cpp b/source/lib/sourcepackage.cpp
new file mode 100644 (file)
index 0000000..3648df7
--- /dev/null
@@ -0,0 +1,273 @@
+#include <cstdlib>
+#include <msp/core/algorithm.h>
+#include <msp/core/maputils.h>
+#include <msp/fs/utils.h>
+#include <msp/io/print.h>
+#include <msp/strings/lexicalcast.h>
+#include <msp/strings/utils.h>
+#include "android/androidapplicationcomponent.h"
+#include "binarycomponent.h"
+#include "binarypackage.h"
+#include "builder.h"
+#include "builtin/compilecommandsjson.h"
+#include "datafile/datapackcomponent.h"
+#include "file.h"
+#include "installcomponent.h"
+#include "builtin/pkgconfigfile.h"
+#include "sourcearchivecomponent.h"
+#include "sourcegenerator.h"
+#include "sourcepackage.h"
+#include "tool.h"
+#include "builtin/vcxprojectfile.h"
+#include "builtin/vssolutionfile.h"
+
+using namespace std;
+using namespace Msp;
+
+SourcePackage::SourcePackage(Builder &b, const string &n, const FS::Path &f):
+       Package(b, n),
+       source_dir(FS::dirname(f)),
+       config(*this),
+       cache(*this)
+{
+       config.load();
+
+       build_file = builder.get_vfs().get_target(f);
+       if(!build_file)
+               build_file = new File(builder, *this, f);
+       source_archive = new SourceArchiveComponent(*this);
+       components.push_back(source_archive);
+}
+
+SourcePackage::~SourcePackage()
+{
+       for(Component *c: components)
+               delete c;
+}
+
+FS::Path SourcePackage::get_temp_directory() const
+{
+       string subdir = builder.get_current_arch().get_name();
+       if(build_type)
+       {
+               subdir += '.';
+               subdir += build_type->get_name();
+       }
+
+       const FS::Path &temp = builder.get_temp_directory();
+       if(temp.is_absolute())
+               return temp/name/subdir;
+       else
+               return source_dir/temp/subdir;
+}
+
+FS::Path SourcePackage::get_output_directory() const
+{
+       const Architecture &arch = builder.get_current_arch();
+       if(arch.is_native())
+               return source_dir;
+       else
+               return source_dir/arch.get_name();
+}
+
+const Component &SourcePackage::get_component(const string &n) const
+{
+       auto i = find_if(components, [&n](const Component *c){ return c->get_name()==n; });
+       if(i!=components.end())
+               return **i;
+       throw key_error(n);
+}
+
+bool SourcePackage::match_feature(const string &feat, const string *comp) const
+{
+       string value = config.get_option("with_"+feat).value;
+       if(comp)
+               return value==*comp;
+       else
+               return lexical_cast<bool>(value);
+}
+
+void SourcePackage::set_build_type(const BuildType &t)
+{
+       build_type = &t;
+}
+
+void SourcePackage::do_prepare()
+{
+       BuildInfo final_build_info;
+
+       if(build_type)
+               final_build_info.update_from(build_type->get_build_info());
+
+       final_build_info.update_from(build_info);
+       build_info = final_build_info;
+
+       build_info.incpath.push_back((builder.get_prefix()/"include").str());
+       build_info.libpath.push_back((builder.get_prefix()/"lib").str());
+
+       for(const Feature &f: features)
+       {
+               string ident = "WITH_"+toupper(f.name);
+               string value = config.get_option("with_"+f.name).value;
+
+               if(f.choices.empty())
+               {
+                       if(!lexical_cast<bool>(value))
+                               continue;
+                       value = "1";
+               }
+
+               build_info.defines[ident] = value;
+               if(f.exported)
+                       export_binfo.defines[ident] = value;
+       }
+
+       for(Component *c: components)
+       {
+               c->prepare();
+               c->create_build_info();
+
+               c->update_exported_build_info(export_binfo);
+       }
+
+       cache.load();
+
+       for(Component *c: components)
+               c->create_targets();
+
+       const Architecture &arch = builder.get_native_arch();
+       if(!export_binfo.libs.empty())
+       {
+               export_binfo.incpath.push_back((builder.get_prefix()/"include").str());
+               export_binfo.libpath.push_back((builder.get_prefix()/"lib").str());
+
+               if(arch.get_system()=="linux")
+               {
+                       PkgConfigFile *pc = new PkgConfigFile(builder, *this);
+                       builder.get_build_graph().get_target("install")->add_dependency(*builder.get_toolchain().get_tool("CP").create_target(*pc));
+               }
+       }
+
+       export_binfo.standards = build_info.standards;
+
+       if(arch.get_system()=="windows")
+       {
+               new VcxProjectFile(builder, *this);
+               new VsSolutionFile(builder, *this);
+       }
+
+       new CompileCommandsJson(builder, *this);
+}
+
+void SourcePackage::save_caches()
+{
+       config.save();
+       cache.save();
+}
+
+
+SourcePackage::Loader::Loader(SourcePackage &p, const Config::InputOptions *o):
+       DataFile::DerivedObjectLoader<SourcePackage, Package::Loader>(p),
+       FeatureConditional(p, p.name),
+       options(o)
+{
+       add("android_application", &Loader::component<AndroidApplicationComponent>);
+       add("build_info",  &Loader::build_info);
+       add("datapack",    &Loader::component<DataPackComponent>);
+       add("description", &SourcePackage::description);
+       add("feature",     &Loader::feature);
+       add("generate",    &Loader::generate);
+       add("install",     &Loader::component<InstallComponent>);
+       add("interface_version", &Loader::interface_version);
+       add("library",     &Loader::component_arg<BinaryComponent, BinaryComponent::Type>, BinaryComponent::LIBRARY);
+       add("module",      &Loader::component_arg<BinaryComponent, BinaryComponent::Type>, BinaryComponent::MODULE);
+       add("program",     &Loader::component_arg<BinaryComponent, BinaryComponent::Type>, BinaryComponent::PROGRAM);
+       add("source_archive", &Loader::source_archive);
+       add("source_tarball", &Loader::source_archive);
+       add("tarball",     &Loader::tarball);
+       add("version",     &Loader::version);
+}
+
+void SourcePackage::Loader::finish()
+{
+       /* Make sure the source tarball is last in the list so targets from all
+       other components wil be created first */
+       auto i = find(obj.components, obj.source_archive);
+       if(i!=obj.components.end())
+       {
+               obj.components.erase(i);
+               obj.components.push_back(obj.source_archive);
+       }
+}
+
+void SourcePackage::Loader::feature(const string &n, const string &d)
+{
+       Feature feat(n);
+       feat.description = d;
+       load_sub(feat);
+       obj.features.push_back(feat);
+
+       const Config::Option &opt = obj.config.add_option(feat);
+       if(options)
+       {
+               auto i = options->find(opt.name);
+               if(i!=options->end())
+                       obj.config.set_option(opt.name, i->second);
+       }
+}
+
+template<typename C>
+void SourcePackage::Loader::component(const string &n)
+{
+       C *comp = new C(obj, n);
+       load_sub(*comp);
+       obj.components.push_back(comp);
+}
+
+template<typename C, typename A>
+void SourcePackage::Loader::component_arg(A a, const string &n)
+{
+       C *comp = new C(obj, n, a);
+       load_sub(*comp);
+       obj.components.push_back(comp);
+}
+
+void SourcePackage::Loader::build_info()
+{
+       load_sub(obj.build_info);
+}
+
+void SourcePackage::Loader::generate(const string &tag)
+{
+       SourceGenerator *gen = new SourceGenerator(obj.builder, obj, tag);
+       load_sub(*gen);
+       obj.local_tools.add_tool(gen);
+}
+
+void SourcePackage::Loader::interface_version(const string &v)
+{
+       obj.interface_version = v;
+       if(obj.version.empty())
+               obj.version = v;
+}
+
+void SourcePackage::Loader::source_archive()
+{
+       load_sub(*obj.source_archive);
+}
+
+void SourcePackage::Loader::tarball(const string &)
+{
+       IO::print("%s: Deprecated tarball component ignored\n", get_source());
+}
+
+void SourcePackage::Loader::version(const string &v)
+{
+       obj.version = v;
+
+       string::size_type i = 0;
+       for(unsigned dots=0; i<obj.version.size(); ++i)
+               if(obj.version[i]=='.' && ++dots>=2)
+                       break;
+       obj.interface_version = obj.version.substr(0, i);
+}
diff --git a/source/lib/sourcepackage.h b/source/lib/sourcepackage.h
new file mode 100644 (file)
index 0000000..8be8ada
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef SOURCEPACKAGE_H_
+#define SOURCEPACKAGE_H_
+
+#include <stdexcept>
+#include <string>
+#include "buildinfo.h"
+#include "cache.h"
+#include "component.h"
+#include "conditionalloader.h"
+#include "config.h"
+#include "feature.h"
+#include "package.h"
+#include "toolchain.h"
+
+class Builder;
+class BuildType;
+class FileTarget;
+class SourceArchiveComponent;
+
+/**
+A package that can be built by Builder.
+*/
+class SourcePackage: public Package
+{
+public:
+       class Loader: public Msp::DataFile::DerivedObjectLoader<SourcePackage, Package::Loader>, public FeatureConditional
+       {
+       private:
+               const Config::InputOptions *options;
+
+       public:
+               Loader(SourcePackage &, const Config::InputOptions *);
+       private:
+               void finish() override;
+
+               void feature(const std::string &, const std::string &);
+               template<typename C>
+               void component(const std::string &);
+               template<typename C, typename A>
+               void component_arg(A, const std::string &);
+               void build_info();
+               void generate(const std::string &);
+               void interface_version(const std::string &);
+               void source_archive();
+               void tarball(const std::string &);
+               void version(const std::string &);
+       };
+
+private:
+       std::string version;
+       std::string interface_version;
+       std::string description;
+
+       FileTarget *build_file;
+       Msp::FS::Path source_dir;
+       const BuildType *build_type = 0;
+       Toolchain local_tools;
+       std::vector<Feature> features;
+       BuildInfo build_info;
+       std::vector<Component *> components;
+       SourceArchiveComponent *source_archive;
+       Config config;
+       mutable Cache cache;
+
+public:
+       SourcePackage(Builder &, const std::string &, const Msp::FS::Path &);
+       ~SourcePackage();
+
+       const std::string &get_version() const { return version; }
+       const std::string &get_interface_version() const { return interface_version; }
+       const std::string &get_description() const { return description; }
+
+       FileTarget &get_build_file() const { return *build_file; }
+       const Msp::FS::Path &get_source_directory() const { return source_dir; }
+       Msp::FS::Path get_temp_directory() const;
+       Msp::FS::Path get_output_directory() const;
+
+       const Toolchain &get_toolchain() const { return local_tools; }
+       const Component &get_component(const std::string &) const;
+       const Config &get_config() const { return config; }
+       bool match_feature(const std::string &, const std::string *) const;
+       void set_build_type(const BuildType &);
+       const BuildInfo &get_build_info() const { return build_info; }
+private:
+       void do_prepare() override;
+
+public:
+       Cache &get_cache() const { return cache; }
+private:
+       void save_caches() override;
+};
+
+#endif
diff --git a/source/lib/staticlibrary.cpp b/source/lib/staticlibrary.cpp
new file mode 100644 (file)
index 0000000..039eb8e
--- /dev/null
@@ -0,0 +1,42 @@
+#include "builder.h"
+#include "component.h"
+#include "objectfile.h"
+#include "sourcepackage.h"
+#include "staticlibrary.h"
+
+using namespace std;
+using namespace Msp;
+
+StaticLibrary::StaticLibrary(Builder &b, const Component &c, const vector<ObjectFile *> &objs):
+       FileTarget(b, c.get_package(), c.get_package().get_output_directory()/generate_filename(c))
+{
+       component = &c;
+       for(ObjectFile *o: objs)
+               add_dependency(*o);
+
+       install_location = "lib";
+       nested_build_sig = true;
+       arch_in_build_sig = true;
+}
+
+string StaticLibrary::generate_filename(const Component &comp)
+{
+       const Architecture &arch = comp.get_package().get_builder().get_current_arch();
+       return arch.create_filename<StaticLibrary>(comp.get_name());
+}
+
+void StaticLibrary::add_required_library(const string &lib)
+{
+       build_info.libs.push_back(lib);
+}
+
+void StaticLibrary::add_library_path(const FS::Path &pth)
+{
+       build_info.libpath.push_back(pth);
+}
+
+void StaticLibrary::collect_build_info(BuildInfo &binfo) const
+{
+       Target::collect_build_info(binfo);
+       binfo.update_from(build_info);
+}
diff --git a/source/lib/staticlibrary.h b/source/lib/staticlibrary.h
new file mode 100644 (file)
index 0000000..98d2990
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STATICLIBRARY_H_
+#define STATICLIBRARY_H_
+
+#include "filetarget.h"
+
+class Component;
+class ObjectFile;
+
+/**
+A static library target.
+*/
+class StaticLibrary: public FileTarget
+{
+private:
+       /* TODO this really belongs in a Component, but some refactoring is required
+       to allow non-builder packages to have components.  Rename BinaryPackage to
+       ExternalPackage, add BuildableComponent and ExternalComponent classes. */
+       BuildInfo build_info;
+
+public:
+       StaticLibrary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { }
+       StaticLibrary(Builder &, const Component &, const std::vector<ObjectFile *> &);
+private:
+       static std::string generate_filename(const Component &);
+
+public:
+       const char *get_type() const override { return "StaticLibrary"; }
+
+       void add_required_library(const std::string &);
+       void add_library_path(const Msp::FS::Path &);
+       void collect_build_info(BuildInfo &) const override;
+};
+
+#endif
diff --git a/source/lib/sysutils.cpp b/source/lib/sysutils.cpp
new file mode 100644 (file)
index 0000000..95867b7
--- /dev/null
@@ -0,0 +1,113 @@
+#define _WIN32_WINNT _WIN32_WINNT_VISTA
+#define WIN32_LEAN_AND_MEAN
+#define INITGUID
+#ifdef _WIN32
+#include <windows.h>
+#include <shlobj.h>
+#include <knownfolders.h>
+#else
+#include <sys/utsname.h>
+#endif
+#include <msp/core/systemerror.h>
+#include <msp/stringcodec/utf16.h>
+#include <msp/stringcodec/utf8.h>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include "sysutils.h"
+
+#if defined(_WIN32) && !defined(PROCESSOR_ARCHITECTURE_ARM64)
+#define PROCESSOR_ARCHITECTURE_ARM64 12
+#endif
+
+using namespace std;
+using namespace Msp;
+
+string get_system_type()
+{
+#ifdef _WIN32
+       SYSTEM_INFO sysinfo;
+       GetSystemInfo(&sysinfo);
+       WORD machine = sysinfo.wProcessorArchitecture;
+       if(machine==PROCESSOR_ARCHITECTURE_AMD64 || machine==PROCESSOR_ARCHITECTURE_INTEL)
+               return "x86-windows";
+       else if(machine==PROCESSOR_ARCHITECTURE_ARM || machine==PROCESSOR_ARCHITECTURE_ARM64)
+               return "arm-windows";
+#else
+       utsname un;
+       if(uname(&un)==0)
+               return tolower(format("%s-%s", un.sysname, un.machine));
+#endif
+
+       return string();
+}
+
+FS::Path get_program_files_x86_dir()
+{
+#ifdef _WIN32
+       wchar_t *program_files_x86_ptr = 0;
+       HRESULT err = SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, 0, NULL, &program_files_x86_ptr);
+       if(err!=S_OK)
+               throw runtime_error("Can't get Program Files path");
+
+       unsigned len = wcslen(program_files_x86_ptr);
+       FS::Path program_files_x86 = StringCodec::transcode<StringCodec::Utf16Le, StringCodec::Utf8>(
+               string(reinterpret_cast<const char *>(program_files_x86_ptr), len*sizeof(wchar_t)));
+
+       CoTaskMemFree(program_files_x86_ptr);
+
+       return program_files_x86;
+#else
+       return "/mnt/c/Program Files (x86)";
+#endif
+}
+
+template<>
+string get_registry_value<string>(const string &path)
+{
+#ifdef _WIN32
+       string::size_type first_sep = path.find('\\');
+       string::size_type last_sep = path.rfind('\\');
+       string root = path.substr(0, first_sep);
+       string key_path = path.substr(first_sep+1, last_sep-first_sep-1);
+       string value_name = path.substr(last_sep+1);
+
+       HKEY root_handle;
+       if(root=="HKCR")
+               root_handle = HKEY_CLASSES_ROOT;
+       else if(root=="HKCC")
+               root_handle = HKEY_CURRENT_CONFIG;
+       else if(root=="HKCU")
+               root_handle = HKEY_CURRENT_USER;
+       else if(root=="HKLM")
+               root_handle = HKEY_LOCAL_MACHINE;
+       else if(root=="HKU")
+               root_handle = HKEY_USERS;
+       else
+               throw invalid_argument("get_registry_value");
+
+       HKEY key;
+       LSTATUS err = RegOpenKeyEx(root_handle, key_path.c_str(), 0, KEY_READ, &key);
+       if(err!=ERROR_SUCCESS)
+               throw Msp::system_error("RegOpenKey", err);
+
+       DWORD value_len;
+       err = RegGetValue(key, 0, value_name.c_str(), RRF_RT_REG_SZ, 0, 0, &value_len);
+       if(err!=ERROR_SUCCESS)
+               throw Msp::system_error("RegGetValue", err);
+
+       char *buffer = new char[value_len];
+       err = RegGetValue(key, 0, value_name.c_str(), RRF_RT_REG_SZ, 0, buffer, &value_len);
+       if(err!=ERROR_SUCCESS)
+       {
+               delete[] buffer;
+               throw Msp::system_error("RegGetValue", err);
+       }
+
+       string result(buffer);
+       delete[] buffer;
+       return result;
+#else
+       (void)path;
+       return string();
+#endif
+}
diff --git a/source/lib/sysutils.h b/source/lib/sysutils.h
new file mode 100644 (file)
index 0000000..7300869
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef SYSUTILS_H_
+#define SYSUTILS_H_
+
+#include <string>
+#include <msp/fs/path.h>
+
+std::string get_system_type();
+Msp::FS::Path get_program_files_x86_dir();
+
+template<typename T>
+T get_registry_value(const std::string &);
+
+#endif
diff --git a/source/lib/target.cpp b/source/lib/target.cpp
new file mode 100644 (file)
index 0000000..dbdd950
--- /dev/null
@@ -0,0 +1,193 @@
+#include <msp/core/algorithm.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include "builder.h"
+#include "filetarget.h"
+#include "sourcepackage.h"
+#include "target.h"
+#include "task.h"
+#include "tool.h"
+
+using namespace std;
+using namespace Msp;
+
+Target::Target(Builder &b, const string &n):
+       builder(b),
+       name(n)
+{
+       builder.get_build_graph().add_target(this);
+}
+
+void Target::add_dependency(Target &dep)
+{
+       if(&dep==this)
+               throw invalid_argument("Target::add_depend");
+       depends.push_back(&dep);
+       if(state>PREPARING)
+               dep.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
+}
+
+void Target::add_transitive_dependency(Target &dep)
+{
+       if(&dep==this)
+               throw invalid_argument("Target::add_transitive_dependency");
+       trans_depends.push_back(&dep);
+}
+
+void Target::add_side_effect(Target &se)
+{
+       side_effects.push_back(&se);
+       if(tool)
+               se.set_tool(*tool);
+       se.primary_target = this;
+       /* Side effects are checked for rebuild after the primary target.  Recheck
+       the primary if a side effect is marked for rebuild. */
+       se.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
+}
+
+Target *Target::get_buildable_target()
+{
+       if(primary_target)
+               return primary_target->get_buildable_target();
+       if(!needs_rebuild())
+               return 0;
+
+       bool self_ok = state!=BUILDING;
+       for(Target *d: depends)
+       {
+               // Avoid infinite recursion if a target depends on its own side effect
+               if(any_equals(side_effects, d))
+                       continue;
+
+               Target *tgt = d->get_buildable_target();
+               if(tgt)
+                       return tgt;
+               else if(d->needs_rebuild())
+                       self_ok = false;
+       }
+
+       if(self_ok)
+               return this;
+
+       return 0;
+}
+
+void Target::set_tool(Tool &t)
+{
+       tool = &t;
+       for(Target *s: side_effects)
+               s->set_tool(t);
+}
+
+void Target::collect_build_info(BuildInfo &binfo) const
+{
+       if(tool)
+               binfo.update_from(tool->get_build_info());
+       if(component)
+               binfo.update_from(component->get_build_info());
+       else if(package)
+               binfo.update_from(package->get_build_info());
+}
+
+void Target::force_rebuild()
+{
+       if(!is_buildable())
+               throw logic_error("Target::force_rebuild");
+       mark_rebuild("Forced rebuild");
+}
+
+void Target::mark_rebuild(const string &reason)
+{
+       if(reason.empty())
+               throw invalid_argument("No reason given for rebuilding "+name);
+
+       state = REBUILD;
+       rebuild_reason = reason;
+
+       builder.get_logger().log("rebuild", "Rebuilding %s: %s", name, reason);
+
+       signal_bubble_rebuild.emit();
+}
+
+void Target::prepare()
+{
+       if(state>PREPARING)
+               return;
+       if(state==PREPARING)
+       {
+               builder.get_logger().log("problems", "Dependency cycle detected at %s", name);
+               problems.push_back("Dependency cycle detected");
+               state = BROKEN;
+               return;
+       }
+
+       state = PREPARING;
+       /* Prepare existing dependencies early, because their information may be
+       needed to find other dependencies. */
+       for(Target *d: depends)
+               d->prepare();
+       if(tool)
+               tool->prepare();
+
+       find_dependencies();
+       bool broken = !problems.empty();
+
+       if(tool)
+       {
+               if(FileTarget *tool_exe = tool->get_executable())
+                       add_dependency(*tool_exe);
+               broken |= !tool->get_problems().empty();
+
+               // Only check package and component problems for buildable targets
+               // XXX How to propagate nested package problems?
+               broken |= (package && !package->get_problems().empty());
+               broken |= (component && !component->get_problems().empty());
+       }
+
+       /* Now that all dependencies are known, prepare them again.  This will do
+       nothing to already prepared targets. */
+       for(Target *d: depends)
+       {
+               d->prepare();
+               broken |= d->is_broken();
+       }
+       for(Target *d: trans_depends)
+               d->prepare();
+
+       check_rebuild();
+       if(broken)
+               state = BROKEN;
+       else if(state==PREPARING)
+               state = UPTODATE;
+
+       for(Target *d: depends)
+               d->signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
+}
+
+Task *Target::build()
+{
+       if(primary_target)
+               return primary_target->build();
+
+       Task *task = tool->run(*this);
+       task->signal_finished.connect(sigc::mem_fun(this, &Target::build_finished));
+       state = BUILDING;
+
+       build(*task);
+       for(Target *s: side_effects)
+               s->build(*task);
+
+       return task;
+}
+
+void Target::build_finished(bool success)
+{
+       state = UPTODATE;
+       if(success)
+       {
+               modified();
+               for(Target *s: side_effects)
+                       s->build_finished(success);
+               signal_modified.emit();
+       }
+}
diff --git a/source/lib/target.h b/source/lib/target.h
new file mode 100644 (file)
index 0000000..6ef6457
--- /dev/null
@@ -0,0 +1,169 @@
+#ifndef TARGET_H_
+#define TARGET_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <sigc++/signal.h>
+#include <msp/time/timestamp.h>
+
+class Builder;
+class BuildInfo;
+class Component;
+class SourcePackage;
+class Task;
+class Tool;
+
+/**
+Targets make up the build graph.  This class is a base for all target types and
+handles many common tasks.  See also FileTarget and VirtualTarget.
+
+Targets can depend on other targets.  There are two kinds of dependencies:
+normal and transitive.  Normal dependencies will need to be built before the
+target itself, and will cause the target to be rebuilt if modified.  Transitive
+dependencies can be used by other targets further down the chain.
+*/
+class Target
+{
+public:
+       using Dependencies = std::vector<Target *>;
+
+protected:
+       enum State
+       {
+               INIT,
+               PREPARING,
+               REBUILD,
+               BUILDING,
+               UPTODATE,
+               BROKEN
+       };
+
+public:
+       sigc::signal<void> signal_bubble_rebuild;
+       sigc::signal<void> signal_modified;
+
+protected:
+       Builder &builder;
+       const SourcePackage *package = 0;
+       const Component *component = 0;
+       std::string name;
+
+       Tool *tool = 0;
+       State state = INIT;
+       std::string rebuild_reason;
+       std::vector<std::string> problems;
+
+       Dependencies depends;
+       Dependencies trans_depends;
+       Dependencies side_effects;
+       Target *primary_target = 0;
+
+       Target(Builder &, const std::string &);
+public:
+       virtual ~Target() { }
+
+       virtual const char *get_type() const = 0;
+       const std::string &get_name() const { return name; }
+       const SourcePackage *get_package() const { return package; }
+       const Component *get_component() const { return component; }
+
+       /** Adds a dependency for the target.  Order is preseved and is important
+       for some target types.  It is an error to create dependency cycles, although
+       this won't be detected until the targets are prepared. */
+       void add_dependency(Target &);
+
+       void add_transitive_dependency(Target &);
+
+       /** Adds a side effect for the target.  Side effects are not built on their
+       own, but together with their primary target. */
+       void add_side_effect(Target &);
+
+protected:
+       /** Finds dependencies for the target.  Called during preparation.  If the
+       target needs to recursively inspect its dependencies, it should prepare its
+       direct dependencies first. */
+       virtual void find_dependencies() { }
+
+public:
+       /// Returns the dependencies of the target, in the order they were added.
+       const Dependencies &get_dependencies() const { return depends; }
+
+       const Dependencies &get_transitive_dependencies() const { return trans_depends; }
+
+       /// Returns the side effects of the target.
+       const Dependencies &get_side_effects() const { return side_effects; }
+
+       /// Returns the primary target associated with a side effect target.
+       Target *get_primary_target() const { return primary_target; }
+
+       /** Tries to locate a target that will help getting this target built.  If
+       all dependencies are up-to-date, returns this target.  If there are no
+       targets ready to be built (maybe because they are being built right now),
+       returns 0. */
+       virtual Target *get_buildable_target();
+
+       /** If this target is a proxy for another (such as InstalledFile), return
+       that target.  Otherwise, return the target itself.  Implementors should call
+       the function recursively to find the final target. */
+       virtual Target *get_real_target() { return this; }
+
+       void set_tool(Tool &);
+
+       /** Returns the tool used to build the target.  To actually build it, call
+       the build() function. */
+       const Tool *get_tool() const { return tool; }
+
+       virtual void collect_build_info(BuildInfo &) const;
+
+       /** Indicates if it's possible to build this target. */
+       bool is_buildable() const { return tool!=0; }
+
+       /** Indicates if this target needs rebuilding.  Only makes sense after the
+       target has been prepared. */
+       bool needs_rebuild() const { return state>PREPARING && state<UPTODATE; }
+
+       /** Returns the reason for rebuilding this target.  Only makes sense after
+       the target has been prepared. */
+       const std::string &get_rebuild_reason() const { return rebuild_reason; }
+
+       /** Forces rebuild of the target. */
+       void force_rebuild();
+
+protected:
+       /** Marks the target to be rebuilt and specified a reason for it. */
+       void mark_rebuild(const std::string &);
+
+       /** Checks if the target needs to be rebuilt and why. */
+       virtual void check_rebuild() = 0;
+
+public:
+       bool is_broken() const { return state==BROKEN; }
+
+       const std::vector<std::string> &get_problems() const { return problems; }
+
+       /** Prepares the target by finding dependencies, recursively preparing them
+       and then checking whether rebuilding is needed. */
+       void prepare();
+
+       /** Invokes the associated Tool to build the target and returns the
+       resulting Task.  The task must be started by the caller. */
+       virtual Task *build();
+
+protected:
+       /** Targets can override this to do additional setup on the Task.  This is
+       also called on side effects, which normally do not get built by themselves. */
+       virtual void build(Task &) { }
+
+       /** Handler for Task::signal_finished. */
+       virtual void build_finished(bool);
+
+       virtual void modified() { }
+
+public:
+       /** Removes any results of building the target. */
+       virtual void clean() { }
+};
+
+#endif
diff --git a/source/lib/task.cpp b/source/lib/task.cpp
new file mode 100644 (file)
index 0000000..e72a741
--- /dev/null
@@ -0,0 +1,35 @@
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include "task.h"
+
+using namespace std;
+using namespace Msp;
+
+void Task::add_file(const FS::Path &f)
+{
+       files.push_back(f);
+}
+
+void Task::set_unlink(bool u)
+{
+       unlink = u;
+}
+
+void Task::prepare()
+{
+       for(const FS::Path &f: files)
+       {
+               if(FS::exists(f))
+               {
+                       // If the file exists, the directory it's in must exist too
+                       FS::unlink(f);
+               }
+               else
+               {
+                       FS::Path dir = FS::dirname(f);
+                       if(!FS::exists(dir))
+                               FS::mkpath(dir, 0755);
+               }
+       }
+}
diff --git a/source/lib/task.h b/source/lib/task.h
new file mode 100644 (file)
index 0000000..915ffe1
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef TASK_H_
+#define TASK_H_
+
+#include <string>
+#include <sigc++/signal.h>
+#include <msp/fs/path.h>
+
+/**
+Tasks are used to manage other programs and worker threads involved in the
+build process.  They are run asynchronously by default, but a wait() method is
+provided should it be necessary to wait for a task to finish.
+*/
+class Task
+{
+public:
+       enum Status
+       {
+               RUNNING,
+               SUCCESS,
+               ERROR
+       };
+
+       sigc::signal<void, bool> signal_finished;
+
+protected:
+       std::vector<Msp::FS::Path> files;
+       bool unlink = false;
+
+       Task() = default;
+public:
+       virtual ~Task() { }
+
+       /** Associate the task with a file. */
+       void add_file(const Msp::FS::Path &);
+
+       /** If set to true, the associated files are removed before the task is
+       started. */
+       void set_unlink(bool = true);
+
+       /** Returns the command being executed for this task.  Only makes sense if
+       an external command is involved. */
+       virtual std::string get_command() const = 0;
+
+       /// Starts the task.
+       virtual void start() = 0;
+
+protected:
+       /// Ensures that the output directory exists and removes files if necessary.
+       void prepare();
+
+public:
+       /// Checks the status of the task and immediately returns.
+       virtual Status check() = 0;
+
+       /// Waits for the task to finish and returns its final status.
+       virtual Status wait() = 0;
+};
+
+#endif
diff --git a/source/lib/templatefile.h b/source/lib/templatefile.h
new file mode 100644 (file)
index 0000000..0912025
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef TEMPLATEFILE_H_
+#define TEMPLATEFILE_H_
+
+#include "sourcefile.h"
+
+/**
+Input file for SourceGenerator.
+*/
+class TemplateFile: public SourceFile
+{
+public:
+       TemplateFile(Builder &b, const Component &c, const Msp::FS::Path &p): SourceFile(b, c, p) { }
+
+       const char *get_type() const override { return "TemplateFile"; }
+};
+
+#endif
diff --git a/source/lib/tool.cpp b/source/lib/tool.cpp
new file mode 100644 (file)
index 0000000..eaf4977
--- /dev/null
@@ -0,0 +1,88 @@
+#include <msp/core/algorithm.h>
+#include <msp/fs/utils.h>
+#include <msp/strings/format.h>
+#include "architecture.h"
+#include "builder.h"
+#include "filetarget.h"
+#include "tool.h"
+
+using namespace std;
+using namespace Msp;
+
+void Tool::set_command(const string &cmd, bool cross)
+{
+       if(cmd.empty())
+               throw invalid_argument("Tool::set_command");
+
+       if(cross && architecture->is_cross() && !FS::Path(cmd).is_absolute())
+               command = format("%s-%s", architecture->get_cross_prefix(), cmd);
+       else
+               command = cmd;
+}
+
+void Tool::set_run(function<Task *(const Target &)> f)
+{
+       run_func = move(f);
+}
+
+bool Tool::accepts_suffix(const string &suffix, bool aux) const
+{
+       return (any_equals(input_suffixes, suffix) || (aux && any_equals(aux_suffixes, suffix)));
+}
+
+Target *Tool::create_target(Target &source, const string &arg)
+{
+       vector<Target *> sources;
+       sources.push_back(&source);
+       return create_target(sources, arg);
+}
+
+void Tool::prepare()
+{
+       if(prepared)
+               return;
+
+       prepared = true;
+
+       if(!command.empty())
+               executable = builder.get_vfs().find_binary(command);
+       prepare(*this);
+       if(!command.empty() && !executable)
+       {
+               builder.get_logger().log("problems", "Can't find executable %s for %s", command, tag);
+               problems.push_back(format("Can't find executable %s", command));
+       }
+}
+
+void Tool::prepare(Tool &tool) const
+{
+       if(&tool!=this && tool.get_base_tool()!=this)
+               throw invalid_argument("Tool::prepare");
+
+       if(&tool!=this && !command.empty() && tool.command.empty())
+               throw logic_error("Derived tool has no command");
+
+       do_prepare(tool);
+}
+
+string Tool::create_build_signature(const BuildInfo &) const
+{
+       if(executable)
+               return format("%s=%s", tag, FS::basename(executable->get_path()));
+       else
+               return string();
+}
+
+
+void operator>>(const LexicalConverter &conv, Tool::ProcessingUnit &unit)
+{
+       const string &str = conv.get();
+       if(str=="FILE")
+               unit = Tool::ONE_FILE;
+       else if(str=="DIRECTORY")
+               unit = Tool::DIRECTORY;
+       else if(str=="COMPONENT")
+               unit = Tool::COMPONENT;
+       else
+               throw lexical_error(format("conversion of '%s' to ProcessingUnit", str));
+}
diff --git a/source/lib/tool.h b/source/lib/tool.h
new file mode 100644 (file)
index 0000000..b756563
--- /dev/null
@@ -0,0 +1,175 @@
+#ifndef TOOL_H_
+#define TOOL_H_
+
+#include <functional>
+#include <string>
+#include <vector>
+#include <msp/fs/path.h>
+#include "buildinfo.h"
+#include "externaltask.h"
+#include "internaltask.h"
+#include "sourcepackage.h"
+#include "target.h"
+#include "virtualfilesystem.h"
+
+class Architecture;
+class Builder;
+class BuildInfo;
+class Component;
+class FileTarget;
+
+class ToolData
+{
+public:
+       VirtualFileSystem::SearchPath system_path;
+       BuildInfo build_info;
+       Msp::Variant extra_data;
+       std::vector<std::string> problems;
+};
+
+/**
+Base class for tools.  Tools are used to turn targets into other targets.
+Examples include compilers and linkers.
+*/
+class Tool: protected ToolData
+{
+public:
+       enum ProcessingUnit
+       {
+               ONE_FILE,
+               DIRECTORY,
+               COMPONENT
+       };
+
+protected:
+       Builder &builder;
+       const Architecture *architecture = 0;
+       std::string tag;
+       std::string command;
+       FileTarget *executable = 0;
+       std::vector<std::string> input_suffixes;
+       std::vector<std::string> aux_suffixes;
+       ProcessingUnit processing_unit = ONE_FILE;
+       std::function<Task *(const Target &)> run_func;
+       bool prepared = false;
+
+       Tool(Builder &b, const std::string &t): Tool(b, 0, t) { }
+       Tool(Builder &b, const Architecture *a, const std::string &t): builder(b), architecture(a), tag(t) { }
+
+public:
+       virtual ~Tool() { }
+
+       Builder &get_builder() const { return builder; }
+
+       const std::string &get_tag() const { return tag; }
+
+       /** Returns the architecture this tool builds for.  May return null if the
+       tool is architecture-agnostic. */
+       const Architecture *get_architecture() const { return architecture; }
+
+       virtual const Tool *get_base_tool() const { return this; }
+
+protected:
+       void set_run(std::function<Task *(const Target &)>);
+
+       template<typename T>
+       void set_run_external(ExternalTask::Arguments (*)(const T &, Msp::FS::Path &));
+
+       template<typename T>
+       void set_run_internal(bool (*)(const T &));
+
+public:
+       /** Overrides the command used by the tool.  The new command should accept
+       the same command line arguments.  Only works on tools that use an external
+       command.  If cross is true and the architecture is not native, a cross
+       prefix is added to the command.  May have no effect after prepare() has been
+       called. */
+       void set_command(const std::string &cmd, bool cross = false);
+
+       /** Returns a target for the tool's own executable.  If the tool does not
+       use an external program, returns null.  The tool must be prepared first. */
+       FileTarget *get_executable() const { return executable; }
+
+       /// Returns a list of suffixes that can be processed with this tool.
+       const std::vector<std::string> &get_input_suffixes() const { return input_suffixes; }
+
+       /** Returns a list of suffixes that are associated with this tool, but can't
+       be processed directly.  For example C and C++ headers. */
+       const std::vector<std::string> &get_auxiliary_suffixes() const { return aux_suffixes; }
+
+       /** Indicates whether the tool can accept a suffix.  If aux is true,
+       auxiliary suffixes are considered as well */
+       bool accepts_suffix(const std::string &, bool aux = false) const;
+
+       /** Returns the grouping unit this tool prefers to process. */
+       ProcessingUnit get_processing_unit() const { return processing_unit; }
+
+       /// Returns the systemwide search path for source files.
+       const VirtualFileSystem::SearchPath &get_system_path() const { return system_path; }
+
+       /** Returns tool-specific build info.  This can be used by other tools down
+       the chain. */
+       const BuildInfo &get_build_info() const { return build_info; }
+
+       const Msp::Variant &get_extra_data() const { return extra_data; }
+
+       /// Creates a source file appropriate for this tool.
+       virtual Target *create_source(const Component &, const Msp::FS::Path &) const { return 0; }
+
+       /** Creates a package-less source file appropriate for this tool.  This is
+       called during dependency discovery when no package has created a target for
+       the file. */
+       virtual Target *create_source(const Msp::FS::Path &) const { return 0; }
+
+       /// Convenience function to create a target from a single source.
+       Target *create_target(Target &, const std::string & = std::string());
+
+       /** Creates a target from sources.  The exact types of accepted sources
+       depends on the tool.  The optional second argument can be used to select an
+       alternative target type for tools that can create multiple kinds of targets. */ 
+       virtual Target *create_target(const std::vector<Target *> &, const std::string & = std::string()) = 0;
+
+       /** Creates an install target for a target created by this tool.  Can return
+       null if the tool does not want to handle installing in a special way. */
+       virtual Target *create_install(Target &) const { return 0; }
+
+       virtual std::string create_build_signature(const BuildInfo &) const;
+
+       void prepare();
+       void prepare(Tool &) const;
+
+protected:
+       virtual void do_prepare(ToolData &) const { }
+
+public:
+       const std::vector<std::string> &get_problems() const { return problems; }
+
+       /** Invokes the tool to build a target.  This should not be called directly;
+       use Target::build() instead. */
+       Task *run(const Target &t) const { return run_func(t); }
+};
+
+
+template<typename T>
+void Tool::set_run_external(ExternalTask::Arguments (*f)(const T &, Msp::FS::Path &))
+{
+       set_run([f](const Target &t){
+               Msp::FS::Path work_dir = t.get_package()->get_source_directory();
+               ExternalTask::Arguments args = f(dynamic_cast<const T &>(t), work_dir);
+               return new ExternalTask(args, work_dir);
+       });
+}
+
+template<typename T>
+void Tool::set_run_internal(bool (*f)(const T &))
+{
+       set_run([f](const Target &t){
+               const T &ct = dynamic_cast<const T &>(t);
+               return new InternalTask([f, &ct]{ return f(ct); });
+       });
+}
+
+
+void operator>>(const Msp::LexicalConverter &, Tool::ProcessingUnit &);
+
+#endif
diff --git a/source/lib/toolchain.cpp b/source/lib/toolchain.cpp
new file mode 100644 (file)
index 0000000..c5b8caa
--- /dev/null
@@ -0,0 +1,58 @@
+#include <msp/core/algorithm.h>
+#include <msp/core/maputils.h>
+#include "tool.h"
+#include "toolchain.h"
+
+using namespace std;
+using namespace Msp;
+
+Toolchain::~Toolchain()
+{
+       for(const auto &kvp: tools)
+               delete kvp.second;
+       for(Toolchain *c: chains)
+               delete c;
+}
+
+void Toolchain::add_tool(Tool *tool)
+{
+       insert_unique(tools, tool->get_tag(), tool);
+}
+
+void Toolchain::add_toolchain(Toolchain *chain)
+{
+       auto i = upper_bound(chains, chain->get_priority(), [](int p, Toolchain *tc){ return p>tc->get_priority(); });
+       chains.insert(i, chain);
+}
+
+bool Toolchain::has_tool(const string &tag) const
+{
+       if(tools.count(tag))
+               return true;
+       return any_of(chains.begin(), chains.end(), [&tag](Toolchain *tc){ return tc->has_tool(tag); });
+}
+
+Tool &Toolchain::get_tool(const string &tag) const
+{
+       if(!tools.count(tag))
+       {
+               for(const Toolchain *c: chains)
+                       if(c->has_tool(tag))
+                               return c->get_tool(tag);
+       }
+
+       return *get_item(tools, tag);
+}
+
+Tool *Toolchain::get_tool_for_suffix(const string &suffix, bool aux) const
+{
+       for(const auto &kvp: tools)
+               if(kvp.second->accepts_suffix(suffix, aux))
+                       return kvp.second;
+
+       for(const Toolchain *c: chains)
+               if(Tool *tool = c->get_tool_for_suffix(suffix, aux))
+                       return tool;
+
+       return 0;
+}
diff --git a/source/lib/toolchain.h b/source/lib/toolchain.h
new file mode 100644 (file)
index 0000000..34fc0a2
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef TOOLCHAIN_H_
+#define TOOLCHAIN_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+class Tool;
+
+/**
+A container for tools.  Performs lookup based on tag or filename extension.
+*/
+class Toolchain
+{
+private:
+       std::string name;
+       int priority = 0;
+       std::map<std::string, Tool *> tools;
+       std::vector<Toolchain *> chains;
+
+protected:
+       Toolchain(const std::string &n, unsigned p): name(n), priority(p) { }
+public:
+       Toolchain() = default;
+       ~Toolchain();
+
+       const std::string &get_name() const { return name; }
+       int get_priority() const { return priority; }
+       void add_tool(Tool *);
+       void add_toolchain(Toolchain *);
+       bool has_tool(const std::string &) const;
+       Tool &get_tool(const std::string &) const;
+       Tool *get_tool_for_suffix(const std::string &, bool = false) const;
+       const std::vector<Toolchain *> &get_toolchains() { return chains; }
+};
+
+#endif
diff --git a/source/lib/virtualfilesystem.cpp b/source/lib/virtualfilesystem.cpp
new file mode 100644 (file)
index 0000000..16d4de8
--- /dev/null
@@ -0,0 +1,189 @@
+#include <msp/core/environ.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/io/print.h>
+#include <msp/strings/utils.h>
+#include "builder.h"
+#include "csourcefile.h"
+#include "executable.h"
+#include "importlibrary.h"
+#include "sharedlibrary.h"
+#include "staticlibrary.h"
+#include "tool.h"
+#include "virtualfilesystem.h"
+
+using namespace std;
+using namespace Msp;
+
+FileTarget *VirtualFileSystem::get_target(const FS::Path &p) const
+{
+       auto i = targets.find(p.str());
+       if(i!=targets.end())
+               return static_cast<FileTarget *>(i->second);
+       return 0;
+}
+
+void VirtualFileSystem::register_path(const FS::Path &path, FileTarget *t)
+{
+       targets.insert({ path, t });
+       nonexistent.erase(path);
+       builder.get_logger().log("vfs", "Path %s registered to %s", path, t->get_name());
+}
+
+FileTarget *VirtualFileSystem::find_header(const string &name, Tool *tool, const SearchPath &path, bool use_syspath)
+{
+       if(!tool)
+               tool = builder.get_toolchain().get_tool_for_suffix(FS::extpart(FS::basename(name)), true);
+       if(!tool)
+               return 0;
+
+       tool->prepare();
+
+       SearchPath combined_path = path;
+       if(use_syspath)
+       {
+               const SearchPath &syspath = tool->get_system_path();
+               combined_path.insert(combined_path.end(), syspath.begin(), syspath.end());
+       }
+
+       for(const FS::Path &p: combined_path)
+       {
+               FS::Path filename = p/name;
+               if(FileTarget *tgt = get_target(filename))
+               {
+                       builder.get_logger().log("vfs", "Header %s found in %s as existing %s", name, p.str(), tgt->get_type());
+                       return tgt;
+               }
+               else if(file_exists(filename))
+               {
+                       builder.get_logger().log("vfs", "Header %s found in %s", name, p.str());
+                       return dynamic_cast<FileTarget *>(tool->create_source(filename));
+               }
+
+               builder.get_logger().log("vfs", "Header %s not found in %s", name, p.str());
+       }
+
+       return 0;
+}
+
+FileTarget *VirtualFileSystem::find_library(const string &lib, const SearchPath &path, BuildInfo::LibraryMode mode, bool use_syspath)
+{
+       SearchPath combined_path = path;
+       if(use_syspath)
+       {
+               Tool &linker = builder.get_toolchain().get_tool("LINK");
+               linker.prepare();
+               const SearchPath &syspath = linker.get_system_path();
+               combined_path.insert(combined_path.end(), syspath.begin(), syspath.end());
+       }
+
+       const Architecture &arch = builder.get_current_arch();
+
+       vector<string> shared_names;
+       bool use_import_lib = false;
+       if(mode!=BuildInfo::FORCE_STATIC)
+       {
+               shared_names = Pattern::apply_list(arch.get_patterns<ImportLibrary>(), lib);
+               if(!(use_import_lib = !shared_names.empty()))
+                       shared_names = Pattern::apply_list(arch.get_patterns<SharedLibrary>(), lib);
+       }
+
+       vector<string> static_names;
+       if(mode!=BuildInfo::FORCE_DYNAMIC)
+               static_names = Pattern::apply_list(arch.get_patterns<StaticLibrary>(), lib);
+
+       for(const FS::Path &p: combined_path)
+       {
+               const vector<string> *cur_names = (mode>=BuildInfo::DYNAMIC ? &shared_names : &static_names);
+               for(auto j=cur_names->begin(); j!=cur_names->end(); )
+               {
+                       FS::Path filename = p / *j;
+                       if(FileTarget *tgt = get_target(filename))
+                       {
+                               builder.get_logger().log("vfs", "Library %s (%s) found in %s as existing %s", lib, *j, p.str(), tgt->get_type());
+                               return tgt;
+                       }
+                       else if(file_exists(filename))
+                       {
+                               builder.get_logger().log("vfs", "Library %s (%s) found in %s", lib, *j, p.str());
+                               if(cur_names==&shared_names)
+                               {
+                                       if(use_import_lib)
+                                               return new ImportLibrary(builder, filename);
+                                       return new SharedLibrary(builder, filename);
+                               }
+                               else
+                                       return new StaticLibrary(builder, filename);
+                       }
+
+                       if(++j==cur_names->end())
+                       {
+                               if(mode==BuildInfo::DYNAMIC && cur_names==&shared_names)
+                                       cur_names = &static_names;
+                               else if(mode==BuildInfo::STATIC && cur_names==&static_names)
+                                       cur_names = &shared_names;
+                               else
+                                       break;
+                               j = cur_names->begin();
+                       }
+               }
+
+               builder.get_logger().log("vfs", "Library %s not found in %s", lib, p.str());
+       }
+
+       return 0;
+}
+
+FileTarget *VirtualFileSystem::find_binary(const string &name)
+{
+       SearchPath path;
+       if(FS::Path(name).is_absolute())
+               path.push_back("/");
+       else
+       {
+               if(sys_bin_path.empty())
+               {
+                       string env_path = Msp::getenv("PATH");
+                       if(!env_path.empty())
+                       {
+                               for(const string &p: split(env_path, ':'))
+                                       sys_bin_path.push_back(p);
+                       }
+                       else
+                       {
+                               sys_bin_path.push_back("/bin");
+                               sys_bin_path.push_back("/usr/bin");
+                       }
+               }
+               path = sys_bin_path;
+       }
+
+       for(const FS::Path &p: path)
+       {
+               FS::Path filename = p/name;
+               if(FileTarget *tgt = get_target(filename))
+               {
+                       builder.get_logger().log("vfs", "Binary %s found in %s as existing %s", name, p, tgt->get_type());
+                       return tgt;
+               }
+               else if(file_exists(filename))
+               {
+                       builder.get_logger().log("vfs", "Binary %s found in %s", name, p);
+                       return new Executable(builder, filename);
+               }
+
+               builder.get_logger().log("vfs", "Binary %s not found in %s", name, p);
+       }
+
+       return 0;
+}
+
+bool VirtualFileSystem::file_exists(const FS::Path &filename)
+{
+       if(nonexistent.count(filename))
+               return false;
+       if(FS::is_reg(filename))
+               return true;
+       nonexistent.insert(filename);
+       return false;
+}
diff --git a/source/lib/virtualfilesystem.h b/source/lib/virtualfilesystem.h
new file mode 100644 (file)
index 0000000..60e198a
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef VIRTUALFILESYSTEM_H_
+#define VIRTUALFILESYSTEM_H_
+
+#include <map>
+#include <set>
+#include <vector>
+#include <msp/fs/path.h>
+#include "buildinfo.h"
+
+class Builder;
+class FileTarget;
+class Pattern;
+class Tool;
+
+/**
+Provides access to the filesystem in a way that takes known targets into
+account.  Thus, targets may be returned for files that do not exist yet if it's
+possible to build them.
+*/
+class VirtualFileSystem
+{
+public:
+       using SearchPath = std::vector<Msp::FS::Path>;
+
+private:
+       Builder &builder;
+       std::map<Msp::FS::Path, FileTarget *> targets;
+       std::set<Msp::FS::Path> nonexistent;
+       SearchPath sys_bin_path;
+
+public:
+       VirtualFileSystem(Builder &b): builder(b) { }
+
+       /** Gets an existing target associated with a path.  If no target has claimed
+       that path, 0 is returned. */
+       FileTarget *get_target(const Msp::FS::Path &) const;
+
+       /** Registers a target with the VFS.  A target may be registered at multiple
+       paths if building it results in multiple files. */
+       void register_path(const Msp::FS::Path &, FileTarget *);
+
+       /** Locates a source file.  If a file is found but no target is associated
+       with it, a new package-less target is created with the appropriate tool.  If
+       use_syspath is true, the system path reported by the tool is also searched. */
+       FileTarget *find_header(const std::string &, Tool *, const SearchPath &, bool use_syspath = true);
+
+       /** Locates a library.  The library name should be the same as what would be
+       used in linking with the library.  If a file is found but no target is
+       associated with it, a new package-less target of appropriate type is
+       created.  If use_syspath is true, the system path reported by the LINK tool
+       is also searched. */
+       FileTarget *find_library(const std::string &, const SearchPath &, BuildInfo::LibraryMode, bool use_syspath = true);
+
+       /** Locates a binary.  The normal search path for binaries is used (usually
+       this means the PATH environment variable).  If a file is found but no target
+       is associated with it, a new package-less Executable target is created. */
+       FileTarget *find_binary(const std::string &);
+
+private:
+       bool file_exists(const Msp::FS::Path &);
+};
+
+#endif
diff --git a/source/lib/virtualtarget.cpp b/source/lib/virtualtarget.cpp
new file mode 100644 (file)
index 0000000..07177d8
--- /dev/null
@@ -0,0 +1,22 @@
+#include <msp/core/algorithm.h>
+#include <msp/fs/path.h>
+#include <msp/fs/utils.h>
+#include "builder.h"
+#include "virtualtarget.h"
+
+using namespace std;
+using namespace Msp;
+
+void VirtualTarget::check_rebuild()
+{
+       // Virtual targets are only rebuilt if their dependencies need rebuilding.
+       auto i = find_if(depends, [](Target *d){ return d->needs_rebuild(); });
+       if(i!=depends.end())
+               mark_rebuild((*i)->get_name()+" needs rebuilding");
+}
+
+Task *VirtualTarget::build()
+{
+       state = UPTODATE;
+       return 0;
+}
diff --git a/source/lib/virtualtarget.h b/source/lib/virtualtarget.h
new file mode 100644 (file)
index 0000000..5151826
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef VIRTUALTARGET_H_
+#define VIRTUALTARGET_H_
+
+#include "target.h"
+
+/**
+A target that is not associated with any file.
+*/
+class VirtualTarget: public Target
+{
+public:
+       VirtualTarget(Builder &b, const std::string &n): Target(b, n) { }
+
+       const char *get_type() const override { return "VirtualTarget"; }
+private:
+       void check_rebuild() override;
+
+public:
+       Task *build() override;
+};
+
+#endif
diff --git a/source/logger.cpp b/source/logger.cpp
deleted file mode 100644 (file)
index 35d4507..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/io/print.h>
-#include "logger.h"
-
-using namespace std;
-using namespace Msp;
-
-void Logger::enable_channel(const string &chan)
-{
-       auto i = lower_bound(enabled_channels, chan);
-       if(i==enabled_channels.end() || *i!=chan)
-               enabled_channels.insert(i, chan);
-}
-
-void Logger::disable_channel(const string &chan)
-{
-       auto i = lower_bound(enabled_channels, chan);
-       if(i!=enabled_channels.end() && *i==chan)
-               enabled_channels.erase(i);
-}
-
-bool Logger::is_channel_enabled(const string &chan) const
-{
-       auto i = lower_bound(enabled_channels, chan);
-       return (i!=enabled_channels.end() && *i==chan);
-}
-
-void Logger::log(const string &chan, const string &message) const
-{
-       if(is_channel_enabled(chan))
-               print(message);
-}
-
-void Logger::print(const string &message) const
-{
-       IO::print("%s\n", message);
-}
diff --git a/source/logger.h b/source/logger.h
deleted file mode 100644 (file)
index 3a10f0e..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef LOGGER_H_
-#define LOGGER_H_
-
-#include <string>
-#include <vector>
-#include <msp/strings/format.h>
-
-class Logger
-{
-private:
-       std::vector<std::string> enabled_channels;
-
-public:
-       void enable_channel(const std::string &);
-       void disable_channel(const std::string &);
-       bool is_channel_enabled(const std::string &) const;
-
-       void log(const std::string &, const std::string &) const;
-
-       template<typename... Args>
-       void log(const std::string &, const std::string &, Args &&...) const;
-
-private:
-       void print(const std::string &) const;
-};
-
-template<typename... Args>
-void Logger::log(const std::string &chan, const std::string &fmt, Args &&... args) const
-{
-       if(is_channel_enabled(chan))
-               print(Msp::format(fmt, std::forward<Args>(args)...));
-}
-
-#endif
diff --git a/source/microsofttools.cpp b/source/microsofttools.cpp
deleted file mode 100644 (file)
index c320ecb..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/fs/dir.h>
-#include <msp/strings/utils.h>
-#include "builder.h"
-#include "externaltask.h"
-#include "logger.h"
-#include "microsofttools.h"
-#include "msvcarchiver.h"
-#include "msvccompiler.h"
-#include "msvclinker.h"
-#include "sysutils.h"
-
-using namespace std;
-using namespace Msp;
-
-MicrosoftTools::MicrosoftTools(Builder &builder, const Architecture &arch):
-       Toolchain("msvc", get_priority(arch))
-{
-       find_vc_bin_dir(builder, arch);
-       find_windows_sdk_dir(builder);
-
-       add_tool(new MsvcCompiler(builder, arch, "CC", *this));
-       add_tool(new MsvcCompiler(builder, arch, "CXX", *this));
-       add_tool(new MsvcLinker(builder, arch, *this));
-       add_tool(new MsvcArchiver(builder, arch, *this));
-}
-
-void MicrosoftTools::find_vc_bin_dir(Builder &builder, const Architecture &arch)
-{
-       FS::Path program_files_x86 = get_program_files_x86_dir();
-
-       ExternalTask::Arguments argv;
-       argv.push_back((program_files_x86/"Microsoft Visual Studio"/"Installer"/"vswhere.exe").str());
-       argv.push_back("-latest");
-       argv.push_back("-property");
-       argv.push_back("installationPath");
-
-       builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
-
-       string output = ExternalTask::run_and_capture_output(argv, FS::Path(), true);
-       FS::Path vs_path = strip(output);
-
-       builder.get_logger().log("tools", "Visual Studio found in %s", vs_path);
-
-       FS::Path vc_aux_build_dir = vs_path/"VC"/"Auxiliary"/"Build";
-       builder.get_logger().log("files", "Traversing %s", vc_aux_build_dir);
-       vector<string> vc_version_files = FS::list_filtered(vc_aux_build_dir, "^Microsoft\\.VCToolsVersion\\.");
-       if(vc_version_files.empty())
-       {
-               builder.get_logger().log("problems", "MSVC tools version not found");
-               return;
-       }
-
-       sort(vc_version_files);
-       FS::Path vc_version_fn = vc_aux_build_dir/vc_version_files.back();
-       builder.get_logger().log("files", "Reading %s", vc_version_fn);
-       char buffer[256];
-       unsigned len = IO::File(vc_version_fn.str()).read(buffer, sizeof(buffer));
-       string vc_version = strip(string(buffer, len));
-
-       builder.get_logger().log("tools", "Detected MSVC version %s", vc_version);
-
-       const Architecture &native_arch = builder.get_native_arch();
-       string host = (native_arch.get_bits()==64 ? "Hostx64" : "Hostx86");
-       string target = (arch.get_bits()==64 ? "x64" : "x86");
-
-       vc_base_dir = vs_path/"VC"/"Tools"/"MSVC"/vc_version;
-       vc_bin_dir = vc_base_dir/"bin"/host/target;
-}
-
-void MicrosoftTools::find_windows_sdk_dir(Builder &builder)
-{
-       win_sdk_dir = get_registry_value<string>("HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0\\InstallationFolder");
-       if(win_sdk_dir.empty())
-               win_sdk_dir = get_program_files_x86_dir()/"Windows Kits"/"10";
-
-       builder.get_logger().log("files", "Traversing %s", win_sdk_dir/"include");
-       vector<string> sdk_versions = FS::list_filtered(win_sdk_dir/"include", "^10\\.");
-       if(sdk_versions.empty())
-       {
-               builder.get_logger().log("problems", "No Windows SDK versions found");
-               return;
-       }
-
-       sort(sdk_versions);
-       win_sdk_version = sdk_versions.back();
-
-       builder.get_logger().log("tools", "Windows SDK version %s found in %s", win_sdk_version, win_sdk_dir);
-}
-
-int MicrosoftTools::get_priority(const Architecture &arch)
-{
-       if(arch.get_toolchain()=="msvc")
-               return 20;
-       else if(arch.get_system()=="windows")
-               return 10;
-       else
-               return 0;
-}
diff --git a/source/microsofttools.h b/source/microsofttools.h
deleted file mode 100644 (file)
index f2e3c37..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef MICROSOFTTOOLS_H_
-#define MICROSOFTTOOLS_H_
-
-#include <msp/fs/path.h>
-#include "toolchain.h"
-
-class Architecture;
-class Builder;
-
-class MicrosoftTools: public Toolchain
-{
-private:
-       Msp::FS::Path vc_base_dir;
-       Msp::FS::Path vc_bin_dir;
-       Msp::FS::Path win_sdk_dir;
-       std::string win_sdk_version;
-
-public:
-       MicrosoftTools(Builder &, const Architecture &);
-
-private:
-       void find_vc_bin_dir(Builder &, const Architecture &);
-       void find_windows_sdk_dir(Builder &);
-
-public:
-       const Msp::FS::Path &get_vc_base_dir() const { return vc_base_dir; }
-       const Msp::FS::Path &get_vc_bin_dir() const { return vc_bin_dir; }
-       const Msp::FS::Path &get_windows_sdk_dir() const { return win_sdk_dir; }
-       const std::string &get_windows_sdk_version() const { return win_sdk_version; }
-
-       static int get_priority(const Architecture &);
-};
-
-#endif
diff --git a/source/mingwdlltool.cpp b/source/mingwdlltool.cpp
deleted file mode 100644 (file)
index c6eddc0..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-#include <cstdlib>
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include "builder.h"
-#include "component.h"
-#include "exportdefinitions.h"
-#include "externaltask.h"
-#include "importlibrary.h"
-#include "installedfile.h"
-#include "mingwdlltool.h"
-#include "objectfile.h"
-#include "sharedlibrary.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-MingwDllTool::MingwDllTool(Builder &b, const Architecture &a):
-       Tool(b, &a, "DLL")
-{
-       set_command("dlltool", true);
-       set_run(_run);
-}
-
-Target *MingwDllTool::create_target(const vector<Target *> &sources, const string &)
-{
-       if(sources.size()!=1)
-               throw invalid_argument("MingwDllTool::create_target");
-       SharedLibrary &shlib = dynamic_cast<SharedLibrary &>(*sources.front());
-
-       vector<ObjectFile *> objs;
-       objs.reserve(shlib.get_dependencies().size());
-       for(Target *d: shlib.get_dependencies())
-               if(ObjectFile *obj = dynamic_cast<ObjectFile *>(d))
-                       objs.push_back(obj);
-
-       ExportDefinitions *exp = new ExportDefinitions(builder, *shlib.get_component(), objs);
-       exp->set_tool(*this);
-
-       ImportLibrary *imp = new ImportLibrary(builder, *shlib.get_component(), shlib, *exp);
-       imp->set_tool(*this);
-
-       return imp;
-}
-
-Target *MingwDllTool::create_install(Target &target) const
-{
-       if(ImportLibrary *imp = dynamic_cast<ImportLibrary *>(&target))
-       {
-               Tool &copy = builder.get_toolchain().get_tool("CP");
-               InstalledFile *inst_tgt = dynamic_cast<InstalledFile *>(copy.create_target(target));
-               string link_name = format("lib%s.dll.a", imp->get_shared_library()->get_libname());
-               if(link_name!=FS::basename(inst_tgt->get_path()))
-                       inst_tgt->set_symlink(link_name);
-               return inst_tgt;
-       }
-       else
-               return 0;
-}
-
-Task *MingwDllTool::_run(const Target &target)
-{
-       const Tool &tool = *target.get_tool();
-
-       const ImportLibrary *imp = dynamic_cast<const ImportLibrary *>(&target);
-       const ExportDefinitions *exp = 0;
-       if(imp)
-               exp = &dynamic_cast<const ExportDefinitions &>(*imp->get_dependencies().front());
-       else
-               exp = dynamic_cast<const ExportDefinitions *>(&target);
-       if(!imp && !exp)
-               throw invalid_argument("MingwDllTool::run");
-
-       vector<string> argv;
-       argv.push_back(tool.get_executable()->get_path().str());
-
-       /* dlltool is stupid and puts temporary files in the working directory by
-       default */
-       argv.push_back("--temp-prefix");
-       char random[8];
-       for(unsigned i=0; i<8; ++i)
-               random[i] = 'a'+(rand()%26);
-       argv.push_back(string("/tmp/")+string(random, 8));
-
-       const Component &comp = *target.get_component();
-       FS::Path work_dir = comp.get_package().get_source_directory();
-
-       if(imp)
-       {
-               const SharedLibrary &shlib = *imp->get_shared_library();
-
-               argv.push_back("-d");
-               argv.push_back(relative(exp->get_path(), work_dir).str());
-
-               argv.push_back("-D");
-               if(shlib.get_install_filename().empty())
-                       argv.push_back(FS::basename(shlib.get_path()));
-               else
-                       argv.push_back(shlib.get_install_filename());
-
-               argv.push_back("-l");
-               argv.push_back(relative(imp->get_path(), work_dir).str());
-       }
-       else
-       {
-               for(Target *d: exp->get_dependencies())
-                       if(ObjectFile *obj = dynamic_cast<ObjectFile *>(d))
-                               argv.push_back(relative(obj->get_path(), work_dir).str());
-
-               // XXX Should use dllexport, but that has some other problems to solve
-               argv.push_back("--export-all-symbols");
-
-               argv.push_back("-z");
-               argv.push_back(relative(exp->get_path(), work_dir).str());
-       }
-
-       return new ExternalTask(argv, work_dir);
-}
diff --git a/source/mingwdlltool.h b/source/mingwdlltool.h
deleted file mode 100644 (file)
index d9fb1cb..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef DLLTOOL_H_
-#define DLLTOOL_H_
-
-#include "tool.h"
-
-class MingwDllTool: public Tool
-{
-public:
-       MingwDllTool(Builder &, const Architecture &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-       Target *create_install(Target &) const override;
-
-private:
-       static Task *_run(const Target &);
-};
-
-#endif
diff --git a/source/msvcarchiver.cpp b/source/msvcarchiver.cpp
deleted file mode 100644 (file)
index d0489cf..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-#include <msp/fs/utils.h>
-#include "component.h"
-#include "microsofttools.h"
-#include "msvcarchiver.h"
-#include "objectfile.h"
-#include "sourcepackage.h"
-#include "staticlibrary.h"
-
-using namespace std;
-using namespace Msp;
-
-MsvcArchiver::MsvcArchiver(Builder &b, const Architecture &a, const MicrosoftTools &m):
-       Tool(b, &a, "AR"),
-       ms_tools(m)
-{
-       input_suffixes.push_back(".o");
-       processing_unit = COMPONENT;
-       set_command((ms_tools.get_vc_bin_dir()/"lib.exe").str(), false);
-       set_run_external(_run);
-}
-
-Target *MsvcArchiver::create_target(const vector<Target *> &sources, const string &)
-{
-       if(sources.empty())
-               throw invalid_argument("MsvcArchiver::create_target");
-
-       vector<ObjectFile *> objs;
-       objs.reserve(sources.size());
-       for(Target *s: sources)
-               objs.push_back(&dynamic_cast<ObjectFile &>(*s));
-
-       const Component &comp = *objs.front()->get_component();
-       StaticLibrary *lib = new StaticLibrary(builder, comp, objs);
-       lib->set_tool(*this);
-       return lib;
-}
-
-ExternalTask::Arguments MsvcArchiver::_run(const StaticLibrary &lib, FS::Path &work_dir)
-{
-       const Tool &tool = *lib.get_tool();
-
-       vector<string> argv;
-       argv.push_back(tool.get_executable()->get_path().str());
-       argv.push_back("/NOLOGO");
-
-       argv.push_back("/OUT:"+relative(lib.get_path(), work_dir).str());
-
-       for(Target *d: lib.get_dependencies())
-               if(ObjectFile *obj = dynamic_cast<ObjectFile *>(d))
-                       argv.push_back(relative(obj->get_path(), work_dir).str());
-
-       return argv;
-}
diff --git a/source/msvcarchiver.h b/source/msvcarchiver.h
deleted file mode 100644 (file)
index 7af9571..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef MSVCARCHIVER_H_
-#define MSVCARCHIVER_H_
-
-#include "tool.h"
-
-class MicrosoftTools;
-class StaticLibrary;
-
-class MsvcArchiver: public Tool
-{
-private:
-       const MicrosoftTools &ms_tools;
-
-public:
-       MsvcArchiver(Builder &, const Architecture &, const MicrosoftTools &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static ExternalTask::Arguments _run(const StaticLibrary &, Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/msvccompiler.cpp b/source/msvccompiler.cpp
deleted file mode 100644 (file)
index 56c99c3..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-#include <msp/core/environ.h>
-#include <msp/core/maputils.h>
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include <msp/strings/utils.h>
-#include "builder.h"
-#include "component.h"
-#include "csourcefile.h"
-#include "microsofttools.h"
-#include "msvccompiler.h"
-#include "objectfile.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-MsvcCompiler::MsvcCompiler(Builder &b, const Architecture &a, const string &t, const MicrosoftTools &m):
-       Tool(b, &a, t),
-       ms_tools(m)
-{
-       if(tag=="CC")
-       {
-               input_suffixes.push_back(".c");
-               aux_suffixes.push_back(".h");
-       }
-       else if(tag=="CXX")
-       {
-               input_suffixes.push_back(".cpp");
-               input_suffixes.push_back(".cc");
-               aux_suffixes.push_back(".hpp");
-       }
-       else
-               throw invalid_argument("MsvcCompiler::MsvcCompiler");
-
-       set_command((ms_tools.get_vc_bin_dir()/"cl.exe").str(), false);
-       set_run_external(_run);
-}
-
-Target *MsvcCompiler::create_source(const Component &comp, const FS::Path &path) const
-{
-       return new CSourceFile(builder, comp, path);
-}
-
-Target *MsvcCompiler::create_source(const FS::Path &path) const
-{
-       return new CSourceFile(builder, path);
-}
-
-Target *MsvcCompiler::create_target(const vector<Target *> &sources, const string &)
-{
-       if(sources.size()!=1)
-               throw invalid_argument("MsvcCompiler::create_target");
-       SourceFile &source = dynamic_cast<SourceFile &>(*sources.front());
-       ObjectFile *obj = new ObjectFile(builder, *source.get_component(), source);
-       obj->set_tool(*this);
-       return obj;
-}
-
-string MsvcCompiler::create_build_signature(const BuildInfo &binfo) const
-{
-       string result = Tool::create_build_signature(binfo);
-       result += ',';
-       if(binfo.debug)
-               result += 'g';
-       if(binfo.optimize)
-       {
-               result += 'O';
-               result += (binfo.optimize<0 ? '1' : binfo.optimize==1 ? 'x' : '2');
-       }
-       if(binfo.libmode<=BuildInfo::STATIC)
-               result += 't';
-       return result;
-}
-
-void MsvcCompiler::do_prepare(ToolData &tool) const
-{
-       const std::string &tool_tag = static_cast<Tool &>(tool).get_tag();
-
-       const FS::Path &vc_base_dir = ms_tools.get_vc_base_dir();
-       tool.system_path.push_back(vc_base_dir/"include");
-
-       const FS::Path &win_sdk_dir = ms_tools.get_windows_sdk_dir();
-       const string &win_sdk_ver = ms_tools.get_windows_sdk_version();
-       tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"ucrt");
-       tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"shared");
-       tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"um");
-
-       string path;
-       for(const FS::Path &p: tool.system_path)
-       {
-               append(path, ";", p.str());
-               builder.get_logger().log("tools", "Got %s system path: %s", tool_tag, p);
-       }
-
-       setenv("INCLUDE", path);
-}
-
-ExternalTask::Arguments MsvcCompiler::_run(const ObjectFile &object, FS::Path &work_dir)
-{
-       const Tool &tool = *object.get_tool();
-
-       ExternalTask::Arguments argv;
-       argv.push_back(tool.get_executable()->get_path().str());
-       argv.push_back("/nologo");
-       argv.push_back("/c");
-
-       BuildInfo binfo;
-       object.collect_build_info(binfo);
-
-       if(binfo.standards.count(tool.get_tag()))
-       {
-               const BuildInfo::LanguageStandard &std = get_item(binfo.standards, tool.get_tag());
-               if((tool.get_tag()=="CXX" && std.year>2011) || (tool.get_tag()=="CC" && std.year>1999))
-                       argv.push_back("/std:"+std.str());
-       }
-
-       if(binfo.warning_level>=1)
-       {
-               argv.push_back(format("/W%d", binfo.warning_level));
-               if(binfo.fatal_warnings)
-                       argv.push_back("/WX");
-               if(binfo.warning_level>=3)
-                       argv.push_back("/permissive-");
-
-               argv.push_back("/wd4068");  // Unknown pragma
-               if(binfo.warning_level<4)
-               {
-                       argv.push_back("/wd4244");  // Narrowing conversion on arg or return
-                       argv.push_back("/wd4267");  // Narrowing conversion
-               }
-       }
-       else
-               argv.push_back("/w");
-
-       for(const FS::Path &p: binfo.local_incpath)
-       {
-               argv.push_back("/I");
-               argv.push_back(p.str());
-       }
-       for(const FS::Path &p: binfo.incpath)
-       {
-               argv.push_back("/I");
-               argv.push_back(p.str());
-       }
-
-       for(const auto &kvp: binfo.defines)
-       {
-               argv.push_back("/D");
-               if(kvp.second.empty())
-                       argv.push_back(kvp.first);
-               else
-                       argv.push_back(format("%s=%s", kvp.first, kvp.second));
-       }
-
-       if(binfo.debug)
-               argv.push_back("/Z7");
-       if(binfo.optimize)
-       {
-               if(binfo.optimize<0)
-                       argv.push_back("/O1");
-               else if(binfo.optimize==1)
-                       argv.push_back("/Ox");
-               else
-                       argv.push_back("/O2");
-       }
-
-       if(binfo.libmode<=BuildInfo::STATIC)
-               argv.push_back(binfo.debug ? "/MTd" : "/MT");
-       else
-               argv.push_back(binfo.debug ? "/MDd" : "/MD");
-
-       argv.push_back("/EHsc");
-
-       FS::Path obj_path = object.get_path();
-       FS::Path src_path = object.get_source().get_path();
-
-       argv.push_back("/Fo"+relative(obj_path, work_dir).str());
-       argv.push_back(relative(src_path, work_dir).str());
-
-       return argv;
-}
diff --git a/source/msvccompiler.h b/source/msvccompiler.h
deleted file mode 100644 (file)
index 2782c23..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef MSVCCOMPILER_H_
-#define MSVCCOMPILER_H_
-
-#include "tool.h"
-
-class MicrosoftTools;
-class ObjectFile;
-
-class MsvcCompiler: public Tool
-{
-private:
-       const MicrosoftTools &ms_tools;
-
-public:
-       MsvcCompiler(Builder &, const Architecture &, const std::string &, const MicrosoftTools &);
-
-       Target *create_source(const Component &, const Msp::FS::Path &) const override;
-       Target *create_source(const Msp::FS::Path &) const override;
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-       std::string create_build_signature(const BuildInfo &) const override;
-
-protected:
-       void do_prepare(ToolData &) const override;
-
-public:
-       static ExternalTask::Arguments _run(const ObjectFile &, Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/msvclinker.cpp b/source/msvclinker.cpp
deleted file mode 100644 (file)
index 9352c0f..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-#include <msp/core/environ.h>
-#include <msp/fs/utils.h>
-#include <msp/strings/utils.h>
-#include "builder.h"
-#include "component.h"
-#include "executable.h"
-#include "importlibrary.h"
-#include "microsofttools.h"
-#include "msvclinker.h"
-#include "objectfile.h"
-#include "sharedlibrary.h"
-#include "sourcepackage.h"
-#include "staticlibrary.h"
-
-using namespace std;
-using namespace Msp;
-
-MsvcLinker::MsvcLinker(Builder &b, const Architecture &a, const MicrosoftTools &m):
-       Tool(b, &a, "LINK"),
-       ms_tools(m)
-{
-       input_suffixes.push_back(".o");
-       input_suffixes.push_back(".a");
-
-       processing_unit = COMPONENT;
-
-       set_command((ms_tools.get_vc_bin_dir()/"link.exe").str(), false);
-       set_run_external(_run);
-}
-
-Target *MsvcLinker::create_target(const vector<Target *> &sources, const string &arg)
-{
-       if(sources.empty())
-               throw invalid_argument("MsvcLinker::create_target");
-
-       vector<ObjectFile *> objs;
-       objs.reserve(sources.size());
-       for(Target *s: sources)
-               objs.push_back(&dynamic_cast<ObjectFile &>(*s));
-
-       const Component &comp = *objs.front()->get_component();
-       Binary *bin = 0;
-       if(arg=="shared")
-               bin = new SharedLibrary(builder, comp, objs);
-       else
-               bin = new Executable(builder, comp, objs);
-       bin->set_tool(*this);
-       return bin;
-}
-
-string MsvcLinker::create_build_signature(const BuildInfo &binfo) const
-{
-       string result = Tool::create_build_signature(binfo);
-       result += ',';
-       if(binfo.strip)
-               result += 's';
-       if(!binfo.libs.empty())
-       {
-               result += ",l";
-               result += join(binfo.libs.begin(), binfo.libs.end(), ",l");
-       }
-       return result;
-}
-
-void MsvcLinker::do_prepare(ToolData &tool) const
-{
-       const std::string &tool_tag = static_cast<Tool &>(tool).get_tag();
-       const Architecture &arch = *static_cast<Tool &>(tool).get_architecture();
-       string arch_dir = (arch.get_bits()==64 ? "x64" : "x86");
-
-       const FS::Path &vc_base_dir = ms_tools.get_vc_base_dir();
-       tool.system_path.push_back(vc_base_dir/"lib"/arch_dir);
-
-       const FS::Path &win_sdk_dir = ms_tools.get_windows_sdk_dir();
-       const string &win_sdk_ver = ms_tools.get_windows_sdk_version();
-       tool.system_path.push_back(win_sdk_dir/"lib"/win_sdk_ver/"ucrt"/arch_dir);
-       tool.system_path.push_back(win_sdk_dir/"lib"/win_sdk_ver/"um"/arch_dir);
-
-       string path;
-       for(const FS::Path &p: tool.system_path)
-       {
-               append(path, ";", p.str());
-               builder.get_logger().log("tools", "Got %s system path: %s", tool_tag, p);
-       }
-
-       setenv("LIB", path);
-}
-
-ExternalTask::Arguments MsvcLinker::_run(const Binary &bin, FS::Path &work_dir)
-{
-       const Tool &tool = *bin.get_tool();
-
-       vector<string> argv;
-       argv.push_back(tool.get_executable()->get_path().str());
-       argv.push_back("/NOLOGO");
-
-       if(dynamic_cast<const SharedLibrary *>(&bin))
-               argv.push_back("/DLL");
-
-       BuildInfo binfo;
-       bin.collect_build_info(binfo);
-
-       /*for(const FS::Path &p: binfo.libpath)
-               argv.push_back("/LIBPATH:"+p.str());*/
-       if(binfo.strip)
-               argv.push_back("/INCREMENTAL:NO");
-       else
-               argv.push_back("/DEBUG:FULL");
-
-       argv.push_back("/OUT:"+relative(bin.get_path(), work_dir).str());
-
-       for(Target *d: bin.get_dependencies())
-       {
-               FileTarget *file = dynamic_cast<FileTarget *>(d);
-               Target *tgt = d->get_real_target();
-
-               if(ObjectFile *obj = dynamic_cast<ObjectFile *>(tgt))
-                       argv.push_back(relative(obj->get_path(), work_dir).str());
-               else if(StaticLibrary *stlib = dynamic_cast<StaticLibrary *>(tgt))
-                       argv.push_back((file?file:stlib)->get_path().str());
-               else if(ImportLibrary *imp = dynamic_cast<ImportLibrary *>(tgt))
-                       argv.push_back((file?file:imp)->get_path().str());
-       }
-
-       argv.push_back("/SUBSYSTEM:CONSOLE");
-
-       return argv;
-}
diff --git a/source/msvclinker.h b/source/msvclinker.h
deleted file mode 100644 (file)
index 4320e48..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef MSVCLINKER_H_
-#define MSVCLINKER_H_
-
-#include "tool.h"
-
-class Binary;
-class MicrosoftTools;
-
-class MsvcLinker: public Tool
-{
-private:
-       const MicrosoftTools &ms_tools;
-
-public:
-       MsvcLinker(Builder &, const Architecture &, const MicrosoftTools &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-       std::string create_build_signature(const BuildInfo &) const override;
-
-protected:
-       void do_prepare(ToolData &data) const override;
-
-public:
-       static ExternalTask::Arguments _run(const Binary &, Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/objcsourcefile.cpp b/source/objcsourcefile.cpp
deleted file mode 100644 (file)
index 953819f..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <msp/strings/regex.h>
-#include "objcsourcefile.h"
-
-using namespace std;
-using namespace Msp;
-
-void ObjCSourceFile::parse_includes(IO::Base &in)
-{
-       static Regex r_include("^[ \t]*#(include|import)[ \t]+([\"<].*)[\">]");
-
-       string line;
-       while(in.getline(line))
-               if(RegMatch match = r_include.match(line))
-                       includes.push_back(match[2].str);
-}
diff --git a/source/objcsourcefile.h b/source/objcsourcefile.h
deleted file mode 100644 (file)
index 37f79a3..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef OBJCSOURCEFILE_H_
-#define OBJCSOURCEFILE_H_
-
-#include "csourcefile.h"
-
-/**
-Represents an Objective-C source file.
-*/
-class ObjCSourceFile: public CSourceFile
-{
-public:
-       using CSourceFile::CSourceFile;
-
-       const char *get_type() const override { return "ObjCSourceFile"; }
-
-protected:
-       void parse_includes(Msp::IO::Base &) override;
-};
-
-#endif
diff --git a/source/objectfile.cpp b/source/objectfile.cpp
deleted file mode 100644 (file)
index 00fa679..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/fs/utils.h>
-#include "builder.h"
-#include "component.h"
-#include "objectfile.h"
-#include "sourcefile.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-ObjectFile::ObjectFile(Builder &b, const Component &c, SourceFile &s):
-       FileTarget(b, c.get_package(), generate_target_path(c, s.get_path())),
-       source(s)
-{
-       component = &c;
-       add_dependency(source);
-}
-
-FS::Path ObjectFile::generate_target_path(const Component &comp, const FS::Path &src)
-{
-       const SourcePackage &pkg = comp.get_package();
-       FS::Path temp_dir = pkg.get_temp_directory();
-       FS::Path rel_src;
-       if(FS::descendant_depth(src, temp_dir)>=0)
-               rel_src = FS::relative(src, temp_dir);
-       else
-               rel_src = FS::relative(src, pkg.get_source_directory());
-       string fn;
-       for(const string &c: rel_src)
-       {
-               if(!fn.empty())
-                       fn += '_';
-               if(c!=".")
-                       fn += c;
-       }
-       const Architecture &arch = comp.get_package().get_builder().get_current_arch();
-       return temp_dir/comp.get_name()/arch.create_filename<ObjectFile>(FS::basepart(fn));
-}
-
-void ObjectFile::set_used_in_shared_library(bool u)
-{
-       used_in_shlib = u;
-}
-
-void ObjectFile::collect_build_info(BuildInfo &binfo) const
-{
-       Target::collect_build_info(binfo);
-       binfo.update_from(component->get_build_info_for_path(source.get_path()));
-}
-
-void ObjectFile::find_dependencies()
-{
-       vector<FileTarget *> headers;
-       find_dependencies(source, headers);
-       for(FileTarget *h: headers)
-       {
-               add_dependency(*h);
-               if(h->get_real_target()->is_buildable())
-                       h->signal_modified.connect(sigc::mem_fun(this, static_cast<void (ObjectFile::*)()>(&ObjectFile::find_dependencies)));
-       }
-}
-
-void ObjectFile::find_dependencies(FileTarget &tgt, vector<FileTarget *> &headers)
-{
-       tgt.prepare();
-
-       FileTarget *rtgt = dynamic_cast<FileTarget *>(tgt.get_real_target());
-       Dependencies deps_to_add = rtgt->get_transitive_dependencies();
-       if(rtgt!=&tgt)
-       {
-               FS::Path inst_dir = rtgt->get_component()->get_install_map().get_install_location(*rtgt);
-               /* The target has been displaced by installing it.  Displace any
-               dependencies that come from the same package as well. */
-               const SourcePackage *tpkg = rtgt->get_package();
-               for(Target *&d: deps_to_add)
-               {
-                       FileTarget *file = dynamic_cast<FileTarget *>(d);
-                       if(file && file->get_package()==tpkg && FS::descendant_depth(file->get_path(), tpkg->get_source_directory())>=0)
-                       {
-                               const Component *tcomp = file->get_component();
-                               FS::Path dep_inst = tcomp->get_install_map().get_install_location(*file);
-                               FS::Path displaced = FS::dirname(tgt.get_path())/FS::relative(dep_inst, inst_dir)/FS::basename(file->get_path());
-                               d = builder.get_vfs().get_target(displaced);
-                               if(!d)
-                               {
-                                       /* If the target was in an overlay directory and the displaced
-                                       dependency is not found, try removing the overlay from the path. */
-                                       string last_dir = FS::basename(FS::dirname(displaced));
-                                       if(any_equals(tcomp->get_overlays(), last_dir))
-                                       {
-                                               displaced = displaced.subpath(0, displaced.size()-2)/FS::basename(file->get_path());
-                                               d = builder.get_vfs().get_target(displaced);
-                                       }
-                               }
-                       }
-               }
-       }
-
-       for(Target *d: deps_to_add)
-               if(FileTarget *file = dynamic_cast<FileTarget *>(d))
-               {
-                       auto i = lower_bound(headers, file);
-                       if(i==headers.end() || *i!=file)
-                       {
-                               headers.insert(i, file);
-                               find_dependencies(*file, headers);
-                       }
-               }
-}
diff --git a/source/objectfile.h b/source/objectfile.h
deleted file mode 100644 (file)
index 9e3ff90..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef OBJECTFILE_H_
-#define OBJECTFILE_H_
-
-#include "filetarget.h"
-
-class SourceFile;
-
-/**
-Object files are compiled from source files.
-*/
-class ObjectFile: public FileTarget
-{
-private:
-       SourceFile &source;
-       bool used_in_shlib = false;
-
-public:
-       ObjectFile(Builder &, const Component &, SourceFile &);
-private:
-       static Msp::FS::Path generate_target_path(const Component &, const Msp::FS::Path &);
-
-public:
-       const char *get_type() const override { return "ObjectFile"; }
-       SourceFile &get_source() const { return source; }
-
-       void set_used_in_shared_library(bool);
-       bool is_used_in_shared_library() const { return used_in_shlib; }
-
-       void collect_build_info(BuildInfo &) const override;
-
-private:
-       void find_dependencies() override;
-
-       void find_dependencies(FileTarget &, std::vector<FileTarget *> &);
-};
-
-#endif
diff --git a/source/package.cpp b/source/package.cpp
deleted file mode 100644 (file)
index 10c2db0..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#include <msp/strings/format.h>
-#include "builder.h"
-#include "package.h"
-
-using namespace std;
-using namespace Msp;
-
-Package::Package(Builder &b, const string &n):
-       builder(b),
-       name(n),
-       label(string(1, toupper(n[0]))+n.substr(1))
-{
-       builder.get_package_manager().add_package(this);
-}
-
-void Package::prepare()
-{
-       if(prepared)
-               return;
-
-       for(Package *r: requires)
-               r->prepare();
-
-       do_prepare();
-       prepared = true;
-}
-
-
-Package::Loader::Loader(Package &p):
-       DataFile::ObjectLoader<Package>(p),
-       ArchitectureConditional(p.builder, p.name)
-{
-       add("label",   &Package::label);
-       add("require", &Loader::require);
-}
-
-void Package::Loader::require(const string &n)
-{
-       Package *req = obj.builder.get_package_manager().find_package(n);
-       if(req)
-               obj.requires.push_back(req);
-       else
-               obj.problems.push_back(format("Required package %s not found", n));
-}
diff --git a/source/package.h b/source/package.h
deleted file mode 100644 (file)
index d13aa73..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef PACKAGE_H_
-#define PACKAGE_H_
-
-#include <string>
-#include <vector>
-#include <msp/datafile/objectloader.h>
-#include "buildinfo.h"
-#include "conditionalloader.h"
-#include "config.h"
-
-class Builder;
-class Package;
-
-/**
-A package is a distributable piece of software.  Package information may be
-obtained in several ways: Build files of source packages, pkg-config for binary
-packages and the builderrc file for binary packages with no pkg-config support.
-*/
-class Package
-{
-public:
-       class Loader: public Msp::DataFile::ObjectLoader<Package>, public ArchitectureConditional
-       {
-       public:
-               Loader(Package &);
-       private:
-               void require(const std::string &);
-       };
-
-       using Requirements = std::vector<Package *>;
-
-protected:
-       Builder &builder;
-
-       std::string name;
-       std::string label;
-
-       Requirements requires;
-       BuildInfo export_binfo;
-       bool prepared = false;
-       std::vector<std::string> problems;
-
-       bool use_pkgconfig = true;
-
-       Package(Builder &, const std::string &);
-public:
-       virtual ~Package() { }
-
-       Builder &get_builder() const { return builder; }
-       const std::string &get_name() const { return name; }
-       const std::string &get_label() const { return label; }
-       const Requirements &get_required_packages() const { return requires; }
-
-       const BuildInfo &get_exported_build_info() const { return export_binfo; }
-
-       /// Indicates whether or not this package supports pkg-config
-       bool uses_pkgconfig() const { return use_pkgconfig; }
-
-       /** Prepares the package for building.  Recursively prepares all required
-       packages, populates build info and creates targets. */
-       void prepare();
-
-protected:
-       virtual void do_prepare() { }
-
-public:
-       bool is_prepared() const { return prepared; }
-
-       const std::vector<std::string> &get_problems() const { return problems; }
-
-       virtual void save_caches() { }
-};
-
-#endif
diff --git a/source/packagemanager.cpp b/source/packagemanager.cpp
deleted file mode 100644 (file)
index 37374b6..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-#include <cstdlib>
-#include <msp/core/algorithm.h>
-#include <msp/fs/dir.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include <msp/io/print.h>
-#include <msp/strings/utils.h>
-#include <msp/time/timedelta.h>
-#include <msp/time/utils.h>
-#include "binarypackage.h"
-#include "builder.h"
-#include "externaltask.h"
-#include "package.h"
-#include "packagemanager.h"
-
-using namespace std;
-using namespace Msp;
-
-PackageManager::~PackageManager()
-{
-       for(const auto &kvp: packages)
-               delete kvp.second;
-}
-
-void PackageManager::append_package_path(const FS::Path &p)
-{
-       pkg_path.push_back(p);
-}
-
-void PackageManager::append_binary_package_path(const FS::Path &p)
-{
-       binpkg_path.push_back(p);
-}
-
-void PackageManager::set_no_externals(bool x)
-{
-       no_externals = x;
-}
-
-void PackageManager::add_package(Package *pkg)
-{
-       auto i = packages.find(pkg->get_name());
-       if(i!=packages.end())
-       {
-               if(i->second!=pkg)
-                       throw invalid_argument("Package name conflict");
-               else
-                       throw logic_error("Package is already managed");
-       }
-
-       if(packages.empty())
-               main_pkg = pkg;
-
-       packages.insert({ pkg->get_name(), pkg });
-}
-
-Package *PackageManager::get_package(const string &name) const
-{
-       auto i = packages.find(name);
-       if(i!=packages.end())
-               return i->second;
-
-       return 0;
-}
-
-Package &PackageManager::get_main_package() const
-{
-       if(!main_pkg)
-               throw logic_error("No packages");
-       return *main_pkg;
-}
-
-Package *PackageManager::find_package(const string &name)
-{
-       if(not_found.count(name))
-               return 0;
-
-       if(Package *pkg = get_package(name))
-               return pkg;
-
-       if(!no_externals)
-       {
-               FS::Path path = get_package_location(name);
-               if(!path.empty())
-               {
-                       builder.load_build_file(path/"Build");
-                       auto i = packages.find(name);
-                       if(i!=packages.end())
-                               return i->second;
-               }
-       }
-
-       FS::Path path = get_binary_package_file(name);
-       if(!path.empty())
-       {
-               builder.load_build_file(path);
-               if(Package *pkg = get_package(name))
-                       return pkg;
-       }
-
-       try
-       {
-               // Package source not found - create a binary package
-               string flags_str = run_pkgconfig(name, "flags");
-               BinaryPackage::Flags flags = split(flags_str);
-               flags_str = run_pkgconfig(name, "staticflags");
-               BinaryPackage::Flags static_flags = split(flags_str);
-               Package *pkg = BinaryPackage::from_flags(builder, name, flags, static_flags);
-               packages.insert({ name, pkg });
-               return pkg;
-       }
-       catch(...)
-       {
-               not_found.insert(name);
-               return 0;
-       }
-}
-
-string PackageManager::run_pkgconfig(const string &pkg, const string &what)
-{
-#ifndef _WIN32
-       if(!env_set)
-       {
-               const FS::Path &prefix = builder.get_prefix();
-               if(prefix.str()!="/usr")
-               {
-                       FS::Path pcdir = prefix/"lib/pkgconfig";
-                       if(const char *pcp = getenv("PKG_CONFIG_PATH"))
-                       {
-                               vector<string> path = split(pcp, ':');
-                               if(!any_equals(path, pcdir.str()))
-                               {
-                                       path.push_back(pcdir.str());
-                                       setenv("PKG_CONFIG_PATH", join(path.begin(), path.end(), ":").c_str(), true);
-                               }
-                       }
-                       else
-                               setenv("PKG_CONFIG_PATH", pcdir.str().c_str(), true);
-               }
-       }
-
-       ExternalTask::Arguments argv;
-       argv.push_back("pkg-config");
-       if(what=="cflags" || what=="libs")
-               argv.push_back("--"+what);
-       else if(what=="flags" || what=="staticflags")
-       {
-               argv.push_back("--cflags");
-               argv.push_back("--libs");
-               if(what=="staticflags")
-                       argv.push_back("--static");
-       }
-       else
-               argv.push_back("--variable="+what);
-       argv.push_back(pkg);
-
-       builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
-
-       return ExternalTask::run_and_capture_output(argv);
-#else
-       (void)pkg;
-       (void)what;
-       return string();
-#endif
-}
-
-FS::Path PackageManager::get_package_location(const string &name)
-{
-       builder.get_logger().log("packagemgr", "Looking for source package %s", name);
-
-       try
-       {
-               // Try to get source directory with pkgconfig
-               string srcdir = strip(run_pkgconfig(name, "source"));
-               if(!srcdir.empty() && FS::exists(FS::Path(srcdir)/"Build"))
-                       return srcdir;
-       }
-       catch(...)
-       { }
-
-       if(pkg_dirs.empty())
-       {
-               for(const FS::Path &p: pkg_path)
-               {
-                       builder.get_logger().log("files", "Traversing %s", p);
-                       unsigned count = 0;
-                       for(const string &f: list_files(p))
-                       {
-                               FS::Path full = p/f;
-                               if(FS::exists(full/"Build"))
-                               {
-                                       pkg_dirs.push_back(full);
-                                       ++count;
-                               }
-                       }
-
-                       builder.get_logger().log("packagemgr", "%d source packages found in %s", count, p);
-               }
-
-               builder.get_logger().log("packagemgr", "%d source packages found", pkg_dirs.size());
-       }
-
-       bool msp = !name.compare(0, 3, "msp");
-       for(const FS::Path &p: pkg_dirs)
-       {
-               string base = FS::basename(p);
-               unsigned dash = base.rfind('-');
-
-               if(!base.compare(0, dash, name))
-                       return p;
-               else if(msp && !base.compare(0, dash, name, 3, string::npos))
-                       return p;
-       }
-
-       return FS::Path();
-}
-
-FS::Path PackageManager::get_binary_package_file(const string &name)
-{
-       builder.get_logger().log("packagemgr", "Looking for binary package %s", name);
-
-       if(binpkg_files.empty())
-       {
-               for(const FS::Path &p: binpkg_path)
-               {
-                       builder.get_logger().log("files", "Traversing %s", p);
-                       vector<string> files = list_filtered(p, "\\.bpk$");
-                       for(const string &f: files)
-                               binpkg_files.push_back(p/f);
-                       builder.get_logger().log("packagemgr", "%d binary packages found in %s", files.size(), p);
-               }
-
-               builder.get_logger().log("packagemgr", "%d binary packages found", binpkg_files.size());
-       }
-
-       auto i = find_if(binpkg_files, [&name](const FS::Path &p){ return FS::basepart(FS::basename(p))==name; });
-       if(i!=binpkg_files.end())
-               return *i;
-
-       return FS::Path();
-}
-
-void PackageManager::save_all_caches() const
-{
-       for(const auto &kvp: packages)
-               kvp.second->save_caches();
-}
diff --git a/source/packagemanager.h b/source/packagemanager.h
deleted file mode 100644 (file)
index fc0aa98..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef PACKAGEMANAGER_H_
-#define PACKAGEMANAGER_H_
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-#include <msp/fs/path.h>
-
-class Builder;
-class Package;
-
-/**
-Keeps track of packages.  Also responsible for locating previously unknown
-packages by name.
-*/
-class PackageManager
-{
-private:
-       Builder &builder;
-       std::vector<Msp::FS::Path> pkg_path;
-       std::vector<Msp::FS::Path> pkg_dirs;
-       std::vector<Msp::FS::Path> binpkg_path;
-       std::vector<Msp::FS::Path> binpkg_files;
-       bool no_externals = false;
-       std::map<std::string, Package *> packages;
-       Package *main_pkg = 0;
-       std::set<std::string> not_found;
-       bool env_set = false;
-
-public:
-       PackageManager(Builder &b): builder(b) { }
-       ~PackageManager();
-
-       /// Adds a location to look for source packages from.
-       void append_package_path(const Msp::FS::Path &);
-
-       /// Adds a location to look for binary packages from.
-       void append_binary_package_path(const Msp::FS::Path &);
-
-       /** Prevent creation of source packages. */
-       void set_no_externals(bool);
-
-       /** Adds a package to the manager.  Called from Package constructor. */
-       void add_package(Package *);
-
-       /** Returns a package from the cache. */
-       Package *get_package(const std::string &) const;
-
-       /** Returns the package that was added first.  This should be considered
-       the primary build target. */
-       Package &get_main_package() const;
-
-       const std::map<std::string, Package *> &get_packages() const { return packages; }
-
-       /** Locates a package and loads it if necessary. */
-       Package *find_package(const std::string &);
-
-private:
-       std::string run_pkgconfig(const std::string &, const std::string &);
-
-       /** Determines the source directory of a package.  Pkg-config is consulted
-       first, and if it fails, the package path is searched for matches.  The
-       package is expected to be located in a directory named after itself. */
-       Msp::FS::Path get_package_location(const std::string &);
-
-       /** Determines the file containing a binary package.  The file is expected
-       to be named after the package. */
-       Msp::FS::Path get_binary_package_file(const std::string &);
-
-public:
-       void save_all_caches() const;
-};
-
-#endif
diff --git a/source/pattern.cpp b/source/pattern.cpp
deleted file mode 100644 (file)
index fec83da..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#include <stdexcept>
-#include "pattern.h"
-
-using namespace std;
-
-Pattern::Pattern(const string &pat)
-{
-       string::size_type percent = pat.find('%');
-       if(percent==string::npos)
-               throw invalid_argument("No percent sign in pattern");
-       prefix = pat.substr(0, percent);
-       suffix = pat.substr(percent+1);
-}
-
-string Pattern::apply(const string &body) const
-{
-       string result = body;
-       if(body.compare(0, prefix.size(), prefix))
-               result = prefix+result;
-       if(body.size()<=suffix.size() || body.compare(body.size()-suffix.size(), suffix.size(), suffix))
-               result += suffix;
-       return result;
-}
-
-vector<string> Pattern::apply_list(const vector<Pattern> &patterns, const string &body)
-{
-       vector<string> result;
-       for(const Pattern &p: patterns)
-               result.push_back(p.apply(body));
-       return result;
-}
diff --git a/source/pattern.h b/source/pattern.h
deleted file mode 100644 (file)
index 8c623bd..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef PATTERN_H_
-#define PATTERN_H_
-
-#include <string>
-#include <vector>
-
-/**
-Stores a filename pattern.  A pattern consists of a prefix and a suffix, and
-can be applied to a body to form a complete filename.  Either or both of the
-prefix and suffix may be empty.
-*/
-class Pattern
-{
-private:
-       std::string prefix;
-       std::string suffix;
-
-public:
-       /** Constructs a pattern from a single string.  The string must have exactly
-       one percent sign (%) to separate the prefix and suffix. */
-       Pattern(const std::string &);
-
-       /** Applies the pattern to a body string. */
-       std::string apply(const std::string &) const;
-
-       /** Applies a list of patterns to the same body. */
-       static std::vector<std::string> apply_list(const std::vector<Pattern> &, const std::string &);
-};
-
-#endif
diff --git a/source/pkgconfigfile.cpp b/source/pkgconfigfile.cpp
deleted file mode 100644 (file)
index 7631f9a..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "builder.h"
-#include "package.h"
-#include "pkgconfigfile.h"
-
-PkgConfigFile::PkgConfigFile(Builder &b, const SourcePackage &p):
-       FileTarget(b, p, p.get_source_directory()/(p.get_name()+".pc"))
-{
-       tool = &builder.get_toolchain().get_tool("PCG");
-
-       install_location = "lib/pkgconfig";
-}
diff --git a/source/pkgconfigfile.h b/source/pkgconfigfile.h
deleted file mode 100644 (file)
index 81c8cd6..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef PKGCONFIGFILE_H_
-#define PKGCONFIGFILE_H_
-
-#include "sourcepackage.h"
-#include "filetarget.h"
-
-/**
-Creates a .pc file to enable other packages fetch build options with pkg-config.
-*/
-class PkgConfigFile: public FileTarget
-{
-public:
-       PkgConfigFile(Builder &, const SourcePackage &);
-
-       const char *get_type() const override { return "PkgConfigFile"; }
-};
-
-#endif
diff --git a/source/pkgconfiggenerator.cpp b/source/pkgconfiggenerator.cpp
deleted file mode 100644 (file)
index 46387d3..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#include <msp/fs/utils.h>
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include "builder.h"
-#include "internaltask.h"
-#include "pkgconfigfile.h"
-#include "pkgconfiggenerator.h"
-
-using namespace std;
-using namespace Msp;
-
-PkgConfigGenerator::PkgConfigGenerator(Builder &b):
-       Tool(b, "PCG")
-{
-       set_run_internal(_run);
-}
-
-Target *PkgConfigGenerator::create_target(const vector<Target *> &, const string &)
-{
-       throw logic_error("Not implemented");
-}
-
-bool PkgConfigGenerator::_run(const PkgConfigFile &pkgc)
-{
-       Builder &builder = pkgc.get_package()->get_builder();
-       const SourcePackage &spkg = *pkgc.get_package();
-
-       IO::BufferedFile out(pkgc.get_path().str(), IO::M_WRITE);
-       IO::print(out, "prefix=%s\n", builder.get_prefix().str());
-       IO::print(out, "source=%s\n\n", spkg.get_source_directory());
-
-       IO::print(out, "Name: %s\n", spkg.get_label());
-       IO::print(out, "Description: %s\n", spkg.get_description());
-       IO::print(out, "Version: %s\n", spkg.get_version());
-
-       IO::print(out, "Requires:");
-       for(const Package *r: spkg.get_required_packages())
-               if(r->uses_pkgconfig())
-                       IO::print(out, " %s", r->get_name());
-       out.put('\n');
-
-       const BuildInfo &binfo = spkg.get_exported_build_info();
-       IO::print(out, "Libs:");
-       for(const FS::Path &p: binfo.libpath)
-               IO::print(out, " -L%s", prefixify(p, builder.get_prefix()));
-       for(const string &l: binfo.libs)
-               IO::print(out, " -l%s", l);
-       if(binfo.threads)
-               out.write("-pthread");
-       out.put('\n');
-
-       IO::print(out, "Cflags:");
-       for(const FS::Path &p: binfo.incpath)
-               IO::print(out, " -I%s", prefixify(p, builder.get_prefix()));
-       for(const auto &kvp: binfo.defines)
-               if(kvp.second.empty())
-                       IO::print(out, " -D%s", kvp.first);
-               else
-                       IO::print(out, " -D%s=%s", kvp.first, kvp.second);
-       out.put('\n');
-
-       return true;
-}
-
-string PkgConfigGenerator::prefixify(const FS::Path &path, const FS::Path &prefix)
-{
-       if(FS::descendant_depth(path, prefix)>=0)
-       {
-               FS::Path rel_path = FS::relative(path, prefix);
-               return "${prefix}"+rel_path.str().substr(1);
-       }
-       else
-               return path.str();
-}
diff --git a/source/pkgconfiggenerator.h b/source/pkgconfiggenerator.h
deleted file mode 100644 (file)
index 761a2c0..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef PKGCONFIGGENERATOR_H_
-#define PKGCONFIGGENERATOR_H_
-
-#include "tool.h"
-
-class PkgConfigFile;
-
-class PkgConfigGenerator: public Tool
-{
-public:
-       PkgConfigGenerator(Builder &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static bool _run(const PkgConfigFile &);
-       static std::string prefixify(const Msp::FS::Path &, const Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/sharedlibrary.cpp b/source/sharedlibrary.cpp
deleted file mode 100644 (file)
index 0673a05..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include "binarycomponent.h"
-#include "builder.h"
-#include "objectfile.h"
-#include "sharedlibrary.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-SharedLibrary::SharedLibrary(Builder &b, const Msp::FS::Path &p):
-       Binary(b, p)
-{
-       libname = FS::basepart(FS::basename(path));
-       if(!libname.compare(0, 3, "lib"))
-               libname = libname.substr(3);
-}
-
-SharedLibrary::SharedLibrary(Builder &b, const Component &c, const vector<ObjectFile *> &objs):
-       Binary(b, c, generate_filename(c), objs),
-       libname(c.get_name()),
-       import_lib(0)
-{
-       if(builder.get_current_arch().get_system()=="windows")
-               install_location = "bin";
-       else
-               install_location = "lib";
-
-       const BinaryComponent &bcomp = dynamic_cast<const BinaryComponent &>(*component);
-       if(bcomp.get_type()==BinaryComponent::MODULE)
-               install_location /= package->get_name();
-       else
-       {
-               const string &version = component->get_package().get_interface_version();
-               if(!version.empty())
-               {
-                       const Architecture &arch = builder.get_current_arch();
-                       if(arch.get_system()=="windows")
-                               soname = arch.create_filename<SharedLibrary>(format("%s-%s", libname, version));
-                       else if(arch.get_system()=="darwin")
-                               soname = arch.create_filename<SharedLibrary>(format("%s.%s", libname, version));
-                       else
-                               soname = format("%s.%s", arch.create_filename<SharedLibrary>(libname), version);
-
-                       install_filename = soname;
-               }
-       }
-
-       for(ObjectFile *o: objects)
-               o->set_used_in_shared_library(true);
-}
-
-string SharedLibrary::generate_filename(const Component &comp)
-{
-       const BinaryComponent &bcomp = dynamic_cast<const BinaryComponent &>(comp);
-       if(bcomp.get_type()==BinaryComponent::MODULE)
-               return comp.get_name()+".dlm";
-       else
-       {
-               const Architecture &arch = comp.get_package().get_builder().get_current_arch();
-               return arch.create_filename<SharedLibrary>(comp.get_name());
-       }
-}
-
-void SharedLibrary::set_import_library(ImportLibrary *imp)
-{
-       import_lib = imp;
-}
diff --git a/source/sharedlibrary.h b/source/sharedlibrary.h
deleted file mode 100644 (file)
index 564b210..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef SHAREDLIB_H_
-#define SHAREDLIB_H_
-
-#include "binary.h"
-
-class ImportLibrary;
-
-/**
-Represents a shared library.  It has two special properties: libname and
-soname.  Libname is the name used by the linker.  Soname is the canonical
-filename of the library, including version number.  If the owning package has
-no version, soname will be empty.
-
-A SharedLibrary can also store a pointer to the associated ImportLibrary, for
-platforms that need one.
-*/
-class SharedLibrary: public Binary
-{
-private:
-       std::string libname;
-       std::string soname;
-       ImportLibrary *import_lib = 0;
-
-public:
-       SharedLibrary(Builder &, const Msp::FS::Path &);
-       SharedLibrary(Builder &, const Component &, const std::vector<ObjectFile *> &);
-private:
-       static std::string generate_filename(const Component &);
-
-public:
-       const char *get_type() const override { return "SharedLibrary"; }
-       const std::string &get_libname() const { return libname; }
-       const std::string &get_soname() const { return soname; }
-
-       void set_import_library(ImportLibrary *);
-       ImportLibrary *get_import_library() const { return import_lib; }
-};
-
-#endif
diff --git a/source/sourcearchivecomponent.cpp b/source/sourcearchivecomponent.cpp
deleted file mode 100644 (file)
index b1e4279..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#include <msp/core/algorithm.h>
-#include "builder.h"
-#include "file.h"
-#include "sourcearchivecomponent.h"
-#include "sourcepackage.h"
-#include "tool.h"
-
-using namespace std;
-using namespace Msp;
-
-SourceArchiveComponent::SourceArchiveComponent(SourcePackage &p):
-       Component(p, p.get_name()+"-source")
-{ }
-
-void SourceArchiveComponent::create_targets() const
-{
-       Builder &builder = package.get_builder();
-
-       vector<Target *> files;
-       files.push_back(&package.get_build_file());
-
-       for(const FS::Path &s: collect_source_files())
-       {
-               FileTarget *file = builder.get_vfs().get_target(s);
-               if(!file)
-                       file = new File(builder, package, s);
-               files.push_back(file);
-       }
-
-       BuildGraph &build_graph = builder.get_build_graph();
-       for(const auto &kvp: build_graph.get_targets())
-               if(kvp.second->get_package()==&package && !kvp.second->is_buildable())
-                       if(!any_equals(files, kvp.second))
-                               files.push_back(kvp.second);
-
-       const Toolchain &toolchain = builder.get_toolchain();
-       string archive_name = package.get_name();
-       if(!package.get_version().empty())
-               archive_name += "-"+package.get_version();
-       archive_name += "-source";
-       Target *result = toolchain.get_tool("TAR").create_target(files, archive_name);
-       build_graph.get_target("archives")->add_dependency(*result);
-}
diff --git a/source/sourcearchivecomponent.h b/source/sourcearchivecomponent.h
deleted file mode 100644 (file)
index 94c1bc5..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef TARBALLCOMPONENT_H_
-#define TARBALLCOMPONENT_H_
-
-#include "component.h"
-
-class SourceArchiveComponent: public Component
-{
-public:
-       SourceArchiveComponent(SourcePackage &);
-
-       void create_targets() const override;
-};
-
-#endif
diff --git a/source/sourcefile.cpp b/source/sourcefile.cpp
deleted file mode 100644 (file)
index b5b84c7..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "component.h"
-#include "sourcefile.h"
-#include "sourcepackage.h"
-
-SourceFile::SourceFile(Builder &b, const Component &c, const Msp::FS::Path &p):
-       FileTarget(b, c.get_package(), p)
-{
-       component = &c;
-}
diff --git a/source/sourcefile.h b/source/sourcefile.h
deleted file mode 100644 (file)
index 16521a5..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef SOURCEFILE_H_
-#define SOURCEFILE_H_
-
-#include "filetarget.h"
-
-class SourceFile: public FileTarget
-{
-protected:
-       SourceFile(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { }
-       SourceFile(Builder &, const Component &, const Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/sourcegenerator.cpp b/source/sourcegenerator.cpp
deleted file mode 100644 (file)
index d30a2bc..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include "builder.h"
-#include "executable.h"
-#include "sourcegenerator.h"
-#include "sourcepackage.h"
-#include "templatefile.h"
-
-using namespace std;
-using namespace Msp;
-
-SourceGenerator::SourceGenerator(Builder &b, const SourcePackage &p, const string &t):
-       Tool(b, t),
-       package(p)
-{
-       set_run_external(&_run);
-}
-
-Target *SourceGenerator::create_source(const Component &comp, const FS::Path &path) const
-{
-       return new TemplateFile(builder, comp, path);
-}
-
-Target *SourceGenerator::create_target(const vector<Target *> &sources, const string &)
-{
-       if(sources.empty())
-               throw invalid_argument("SourceGenerator::create_target");
-       if(out_suffixes.empty())
-               throw logic_error("No output suffixes");
-
-       TemplateFile &tmpl = dynamic_cast<TemplateFile &>(*sources.front());
-       const Component *comp = tmpl.get_component();
-       const SourcePackage *pkg = tmpl.get_package();
-       FS::Path subdir;
-       string base;
-       if(processing_unit==COMPONENT)
-               base = comp->get_name();
-       else
-       {
-               subdir = FS::dirname(FS::relative(tmpl.get_path(), pkg->get_source_directory()));
-               if(processing_unit==ONE_FILE)
-                       base = FS::basepart(FS::basename(tmpl.get_path()));
-               else if(processing_unit==DIRECTORY)
-               {
-                       base = FS::basename(subdir);
-                       subdir = FS::dirname(subdir);
-               }
-       }
-
-       Target *primary = 0;
-       for(const string &s: out_suffixes)
-       {
-               Tool *tool = builder.get_toolchain().get_tool_for_suffix(s, true);
-               if(tool)
-               {
-                       FS::Path fn = pkg->get_temp_directory()/"generated"/subdir/(base+s);
-                       Target *target = tool->create_source(*comp, fn);
-                       target->set_tool(*this);
-                       for(Target *t: sources)
-                               target->add_dependency(*t);
-                       if(primary)
-                               primary->add_side_effect(*target);
-                       else
-                               primary = target;
-               }
-               else
-                       throw runtime_error("No tool found for suffix "+s);
-       }
-
-       return primary;
-}
-
-ExternalTask::Arguments SourceGenerator::_run(const SourceFile &out_src, FS::Path &work_dir)
-{
-       const SourceGenerator &tool = dynamic_cast<const SourceGenerator &>(*out_src.get_tool());
-
-       vector<string> args;
-       args.push_back(tool.get_executable()->get_path().str());
-       args.insert(args.end(), tool.arguments.begin(), tool.arguments.end());
-
-       for(const Target *d: out_src.get_dependencies())
-               if(const TemplateFile *tmpl = dynamic_cast<const TemplateFile *>(d))
-                       args.push_back(FS::relative(tmpl->get_path(), work_dir).str());
-
-       if(!tool.out_argument.empty())
-               args.push_back(tool.out_argument);
-       args.push_back(FS::relative(out_src.get_path(), work_dir).str());
-
-       return args;
-}
-
-
-SourceGenerator::Loader::Loader(SourceGenerator &sg):
-       DataFile::ObjectLoader<SourceGenerator>(sg),
-       ConditionalLoader(sg.package, format("%s/%s", sg.package.get_name(), sg.tag))
-{
-       add("argument",   &Loader::argument);
-       add("arguments",  &Loader::arguments);
-       add("command",    &Loader::command);
-       add("in_suffix",  &Loader::in_suffix);
-       add("out_argument", &SourceGenerator::out_argument);
-       add("out_suffix", &Loader::out_suffix);
-       add("processing_unit", static_cast<ProcessingUnit SourceGenerator::*>(&SourceGenerator::processing_unit));
-}
-
-void SourceGenerator::Loader::argument(const string &a)
-{
-       obj.arguments.push_back(a);
-}
-
-void SourceGenerator::Loader::arguments(const vector<string> &a)
-{
-       obj.arguments.insert(obj.arguments.end(), a.begin(), a.end());
-}
-
-void SourceGenerator::Loader::command(const string &c)
-{
-       if(c.find('/')!=string::npos)
-               obj.set_command((obj.package.get_source_directory()/c).str());
-       else
-               obj.set_command(c);
-}
-
-void SourceGenerator::Loader::in_suffix(const string &s)
-{
-       obj.input_suffixes.push_back(s);
-}
-
-void SourceGenerator::Loader::out_suffix(const string &s)
-{
-       obj.out_suffixes.push_back(s);
-}
diff --git a/source/sourcegenerator.h b/source/sourcegenerator.h
deleted file mode 100644 (file)
index b8dee24..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef SOURCEGENERATOR_H_
-#define SOURCEGENERATOR_H_
-
-#include <msp/datafile/objectloader.h>
-#include "conditionalloader.h"
-#include "sourcepackage.h"
-#include "tool.h"
-
-class SourceFile;
-
-class SourceGenerator: public Tool
-{
-public:
-       class Loader: public Msp::DataFile::ObjectLoader<SourceGenerator>, public ConditionalLoader
-       {
-       public:
-               Loader(SourceGenerator &);
-
-       private:
-               void argument(const std::string &);
-               void arguments(const std::vector<std::string> &);
-               void command(const std::string &);
-               void in_suffix(const std::string &);
-               void out_argument(const std::string &);
-               void out_suffix(const std::string &);
-       };
-
-private:
-       const SourcePackage &package;
-       std::vector<std::string> out_suffixes;
-       std::vector<std::string> arguments;
-       std::string out_argument;
-
-public:
-       SourceGenerator(Builder &, const SourcePackage &, const std::string &);
-
-       Target *create_source(const Component &, const Msp::FS::Path &) const override;
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static ExternalTask::Arguments _run(const SourceFile &, Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/sourcepackage.cpp b/source/sourcepackage.cpp
deleted file mode 100644 (file)
index 2c478a8..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-#include <cstdlib>
-#include <msp/core/algorithm.h>
-#include <msp/core/maputils.h>
-#include <msp/fs/utils.h>
-#include <msp/io/print.h>
-#include <msp/strings/lexicalcast.h>
-#include <msp/strings/utils.h>
-#include "androidapplicationcomponent.h"
-#include "binarycomponent.h"
-#include "binarypackage.h"
-#include "builder.h"
-#include "compilecommandsjson.h"
-#include "datapackcomponent.h"
-#include "file.h"
-#include "installcomponent.h"
-#include "pkgconfigfile.h"
-#include "sourcearchivecomponent.h"
-#include "sourcegenerator.h"
-#include "sourcepackage.h"
-#include "tool.h"
-#include "vcxprojectfile.h"
-#include "vssolutionfile.h"
-
-using namespace std;
-using namespace Msp;
-
-SourcePackage::SourcePackage(Builder &b, const string &n, const FS::Path &f):
-       Package(b, n),
-       source_dir(FS::dirname(f)),
-       config(*this),
-       cache(*this)
-{
-       config.load();
-
-       build_file = builder.get_vfs().get_target(f);
-       if(!build_file)
-               build_file = new File(builder, *this, f);
-       source_archive = new SourceArchiveComponent(*this);
-       components.push_back(source_archive);
-}
-
-SourcePackage::~SourcePackage()
-{
-       for(Component *c: components)
-               delete c;
-}
-
-FS::Path SourcePackage::get_temp_directory() const
-{
-       string subdir = builder.get_current_arch().get_name();
-       if(build_type)
-       {
-               subdir += '.';
-               subdir += build_type->get_name();
-       }
-
-       const FS::Path &temp = builder.get_temp_directory();
-       if(temp.is_absolute())
-               return temp/name/subdir;
-       else
-               return source_dir/temp/subdir;
-}
-
-FS::Path SourcePackage::get_output_directory() const
-{
-       const Architecture &arch = builder.get_current_arch();
-       if(arch.is_native())
-               return source_dir;
-       else
-               return source_dir/arch.get_name();
-}
-
-const Component &SourcePackage::get_component(const string &n) const
-{
-       auto i = find_if(components, [&n](const Component *c){ return c->get_name()==n; });
-       if(i!=components.end())
-               return **i;
-       throw key_error(n);
-}
-
-bool SourcePackage::match_feature(const string &feat, const string *comp) const
-{
-       string value = config.get_option("with_"+feat).value;
-       if(comp)
-               return value==*comp;
-       else
-               return lexical_cast<bool>(value);
-}
-
-void SourcePackage::set_build_type(const BuildType &t)
-{
-       build_type = &t;
-}
-
-void SourcePackage::do_prepare()
-{
-       BuildInfo final_build_info;
-
-       if(build_type)
-               final_build_info.update_from(build_type->get_build_info());
-
-       final_build_info.update_from(build_info);
-       build_info = final_build_info;
-
-       build_info.incpath.push_back((builder.get_prefix()/"include").str());
-       build_info.libpath.push_back((builder.get_prefix()/"lib").str());
-
-       for(const Feature &f: features)
-       {
-               string ident = "WITH_"+toupper(f.name);
-               string value = config.get_option("with_"+f.name).value;
-
-               if(f.choices.empty())
-               {
-                       if(!lexical_cast<bool>(value))
-                               continue;
-                       value = "1";
-               }
-
-               build_info.defines[ident] = value;
-               if(f.exported)
-                       export_binfo.defines[ident] = value;
-       }
-
-       for(Component *c: components)
-       {
-               c->prepare();
-               c->create_build_info();
-
-               c->update_exported_build_info(export_binfo);
-       }
-
-       cache.load();
-
-       for(Component *c: components)
-               c->create_targets();
-
-       const Architecture &arch = builder.get_native_arch();
-       if(!export_binfo.libs.empty())
-       {
-               export_binfo.incpath.push_back((builder.get_prefix()/"include").str());
-               export_binfo.libpath.push_back((builder.get_prefix()/"lib").str());
-
-               if(arch.get_system()=="linux")
-               {
-                       PkgConfigFile *pc = new PkgConfigFile(builder, *this);
-                       builder.get_build_graph().get_target("install")->add_dependency(*builder.get_toolchain().get_tool("CP").create_target(*pc));
-               }
-       }
-
-       export_binfo.standards = build_info.standards;
-
-       if(arch.get_system()=="windows")
-       {
-               new VcxProjectFile(builder, *this);
-               new VsSolutionFile(builder, *this);
-       }
-
-       new CompileCommandsJson(builder, *this);
-}
-
-void SourcePackage::save_caches()
-{
-       config.save();
-       cache.save();
-}
-
-
-SourcePackage::Loader::Loader(SourcePackage &p, const Config::InputOptions *o):
-       DataFile::DerivedObjectLoader<SourcePackage, Package::Loader>(p),
-       FeatureConditional(p, p.name),
-       options(o)
-{
-       add("android_application", &Loader::component<AndroidApplicationComponent>);
-       add("build_info",  &Loader::build_info);
-       add("datapack",    &Loader::component<DataPackComponent>);
-       add("description", &SourcePackage::description);
-       add("feature",     &Loader::feature);
-       add("generate",    &Loader::generate);
-       add("install",     &Loader::component<InstallComponent>);
-       add("interface_version", &Loader::interface_version);
-       add("library",     &Loader::component_arg<BinaryComponent, BinaryComponent::Type>, BinaryComponent::LIBRARY);
-       add("module",      &Loader::component_arg<BinaryComponent, BinaryComponent::Type>, BinaryComponent::MODULE);
-       add("program",     &Loader::component_arg<BinaryComponent, BinaryComponent::Type>, BinaryComponent::PROGRAM);
-       add("source_archive", &Loader::source_archive);
-       add("source_tarball", &Loader::source_archive);
-       add("tarball",     &Loader::tarball);
-       add("version",     &Loader::version);
-}
-
-void SourcePackage::Loader::finish()
-{
-       /* Make sure the source tarball is last in the list so targets from all
-       other components wil be created first */
-       auto i = find(obj.components, obj.source_archive);
-       if(i!=obj.components.end())
-       {
-               obj.components.erase(i);
-               obj.components.push_back(obj.source_archive);
-       }
-}
-
-void SourcePackage::Loader::feature(const string &n, const string &d)
-{
-       Feature feat(n);
-       feat.description = d;
-       load_sub(feat);
-       obj.features.push_back(feat);
-
-       const Config::Option &opt = obj.config.add_option(feat);
-       if(options)
-       {
-               auto i = options->find(opt.name);
-               if(i!=options->end())
-                       obj.config.set_option(opt.name, i->second);
-       }
-}
-
-template<typename C>
-void SourcePackage::Loader::component(const string &n)
-{
-       C *comp = new C(obj, n);
-       load_sub(*comp);
-       obj.components.push_back(comp);
-}
-
-template<typename C, typename A>
-void SourcePackage::Loader::component_arg(A a, const string &n)
-{
-       C *comp = new C(obj, n, a);
-       load_sub(*comp);
-       obj.components.push_back(comp);
-}
-
-void SourcePackage::Loader::build_info()
-{
-       load_sub(obj.build_info);
-}
-
-void SourcePackage::Loader::generate(const string &tag)
-{
-       SourceGenerator *gen = new SourceGenerator(obj.builder, obj, tag);
-       load_sub(*gen);
-       obj.local_tools.add_tool(gen);
-}
-
-void SourcePackage::Loader::interface_version(const string &v)
-{
-       obj.interface_version = v;
-       if(obj.version.empty())
-               obj.version = v;
-}
-
-void SourcePackage::Loader::source_archive()
-{
-       load_sub(*obj.source_archive);
-}
-
-void SourcePackage::Loader::tarball(const string &)
-{
-       IO::print("%s: Deprecated tarball component ignored\n", get_source());
-}
-
-void SourcePackage::Loader::version(const string &v)
-{
-       obj.version = v;
-
-       string::size_type i = 0;
-       for(unsigned dots=0; i<obj.version.size(); ++i)
-               if(obj.version[i]=='.' && ++dots>=2)
-                       break;
-       obj.interface_version = obj.version.substr(0, i);
-}
diff --git a/source/sourcepackage.h b/source/sourcepackage.h
deleted file mode 100644 (file)
index 8be8ada..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-#ifndef SOURCEPACKAGE_H_
-#define SOURCEPACKAGE_H_
-
-#include <stdexcept>
-#include <string>
-#include "buildinfo.h"
-#include "cache.h"
-#include "component.h"
-#include "conditionalloader.h"
-#include "config.h"
-#include "feature.h"
-#include "package.h"
-#include "toolchain.h"
-
-class Builder;
-class BuildType;
-class FileTarget;
-class SourceArchiveComponent;
-
-/**
-A package that can be built by Builder.
-*/
-class SourcePackage: public Package
-{
-public:
-       class Loader: public Msp::DataFile::DerivedObjectLoader<SourcePackage, Package::Loader>, public FeatureConditional
-       {
-       private:
-               const Config::InputOptions *options;
-
-       public:
-               Loader(SourcePackage &, const Config::InputOptions *);
-       private:
-               void finish() override;
-
-               void feature(const std::string &, const std::string &);
-               template<typename C>
-               void component(const std::string &);
-               template<typename C, typename A>
-               void component_arg(A, const std::string &);
-               void build_info();
-               void generate(const std::string &);
-               void interface_version(const std::string &);
-               void source_archive();
-               void tarball(const std::string &);
-               void version(const std::string &);
-       };
-
-private:
-       std::string version;
-       std::string interface_version;
-       std::string description;
-
-       FileTarget *build_file;
-       Msp::FS::Path source_dir;
-       const BuildType *build_type = 0;
-       Toolchain local_tools;
-       std::vector<Feature> features;
-       BuildInfo build_info;
-       std::vector<Component *> components;
-       SourceArchiveComponent *source_archive;
-       Config config;
-       mutable Cache cache;
-
-public:
-       SourcePackage(Builder &, const std::string &, const Msp::FS::Path &);
-       ~SourcePackage();
-
-       const std::string &get_version() const { return version; }
-       const std::string &get_interface_version() const { return interface_version; }
-       const std::string &get_description() const { return description; }
-
-       FileTarget &get_build_file() const { return *build_file; }
-       const Msp::FS::Path &get_source_directory() const { return source_dir; }
-       Msp::FS::Path get_temp_directory() const;
-       Msp::FS::Path get_output_directory() const;
-
-       const Toolchain &get_toolchain() const { return local_tools; }
-       const Component &get_component(const std::string &) const;
-       const Config &get_config() const { return config; }
-       bool match_feature(const std::string &, const std::string *) const;
-       void set_build_type(const BuildType &);
-       const BuildInfo &get_build_info() const { return build_info; }
-private:
-       void do_prepare() override;
-
-public:
-       Cache &get_cache() const { return cache; }
-private:
-       void save_caches() override;
-};
-
-#endif
diff --git a/source/staticlibrary.cpp b/source/staticlibrary.cpp
deleted file mode 100644 (file)
index 039eb8e..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "builder.h"
-#include "component.h"
-#include "objectfile.h"
-#include "sourcepackage.h"
-#include "staticlibrary.h"
-
-using namespace std;
-using namespace Msp;
-
-StaticLibrary::StaticLibrary(Builder &b, const Component &c, const vector<ObjectFile *> &objs):
-       FileTarget(b, c.get_package(), c.get_package().get_output_directory()/generate_filename(c))
-{
-       component = &c;
-       for(ObjectFile *o: objs)
-               add_dependency(*o);
-
-       install_location = "lib";
-       nested_build_sig = true;
-       arch_in_build_sig = true;
-}
-
-string StaticLibrary::generate_filename(const Component &comp)
-{
-       const Architecture &arch = comp.get_package().get_builder().get_current_arch();
-       return arch.create_filename<StaticLibrary>(comp.get_name());
-}
-
-void StaticLibrary::add_required_library(const string &lib)
-{
-       build_info.libs.push_back(lib);
-}
-
-void StaticLibrary::add_library_path(const FS::Path &pth)
-{
-       build_info.libpath.push_back(pth);
-}
-
-void StaticLibrary::collect_build_info(BuildInfo &binfo) const
-{
-       Target::collect_build_info(binfo);
-       binfo.update_from(build_info);
-}
diff --git a/source/staticlibrary.h b/source/staticlibrary.h
deleted file mode 100644 (file)
index 98d2990..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef STATICLIBRARY_H_
-#define STATICLIBRARY_H_
-
-#include "filetarget.h"
-
-class Component;
-class ObjectFile;
-
-/**
-A static library target.
-*/
-class StaticLibrary: public FileTarget
-{
-private:
-       /* TODO this really belongs in a Component, but some refactoring is required
-       to allow non-builder packages to have components.  Rename BinaryPackage to
-       ExternalPackage, add BuildableComponent and ExternalComponent classes. */
-       BuildInfo build_info;
-
-public:
-       StaticLibrary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { }
-       StaticLibrary(Builder &, const Component &, const std::vector<ObjectFile *> &);
-private:
-       static std::string generate_filename(const Component &);
-
-public:
-       const char *get_type() const override { return "StaticLibrary"; }
-
-       void add_required_library(const std::string &);
-       void add_library_path(const Msp::FS::Path &);
-       void collect_build_info(BuildInfo &) const override;
-};
-
-#endif
diff --git a/source/sysutils.cpp b/source/sysutils.cpp
deleted file mode 100644 (file)
index 95867b7..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-#define _WIN32_WINNT _WIN32_WINNT_VISTA
-#define WIN32_LEAN_AND_MEAN
-#define INITGUID
-#ifdef _WIN32
-#include <windows.h>
-#include <shlobj.h>
-#include <knownfolders.h>
-#else
-#include <sys/utsname.h>
-#endif
-#include <msp/core/systemerror.h>
-#include <msp/stringcodec/utf16.h>
-#include <msp/stringcodec/utf8.h>
-#include <msp/strings/format.h>
-#include <msp/strings/utils.h>
-#include "sysutils.h"
-
-#if defined(_WIN32) && !defined(PROCESSOR_ARCHITECTURE_ARM64)
-#define PROCESSOR_ARCHITECTURE_ARM64 12
-#endif
-
-using namespace std;
-using namespace Msp;
-
-string get_system_type()
-{
-#ifdef _WIN32
-       SYSTEM_INFO sysinfo;
-       GetSystemInfo(&sysinfo);
-       WORD machine = sysinfo.wProcessorArchitecture;
-       if(machine==PROCESSOR_ARCHITECTURE_AMD64 || machine==PROCESSOR_ARCHITECTURE_INTEL)
-               return "x86-windows";
-       else if(machine==PROCESSOR_ARCHITECTURE_ARM || machine==PROCESSOR_ARCHITECTURE_ARM64)
-               return "arm-windows";
-#else
-       utsname un;
-       if(uname(&un)==0)
-               return tolower(format("%s-%s", un.sysname, un.machine));
-#endif
-
-       return string();
-}
-
-FS::Path get_program_files_x86_dir()
-{
-#ifdef _WIN32
-       wchar_t *program_files_x86_ptr = 0;
-       HRESULT err = SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, 0, NULL, &program_files_x86_ptr);
-       if(err!=S_OK)
-               throw runtime_error("Can't get Program Files path");
-
-       unsigned len = wcslen(program_files_x86_ptr);
-       FS::Path program_files_x86 = StringCodec::transcode<StringCodec::Utf16Le, StringCodec::Utf8>(
-               string(reinterpret_cast<const char *>(program_files_x86_ptr), len*sizeof(wchar_t)));
-
-       CoTaskMemFree(program_files_x86_ptr);
-
-       return program_files_x86;
-#else
-       return "/mnt/c/Program Files (x86)";
-#endif
-}
-
-template<>
-string get_registry_value<string>(const string &path)
-{
-#ifdef _WIN32
-       string::size_type first_sep = path.find('\\');
-       string::size_type last_sep = path.rfind('\\');
-       string root = path.substr(0, first_sep);
-       string key_path = path.substr(first_sep+1, last_sep-first_sep-1);
-       string value_name = path.substr(last_sep+1);
-
-       HKEY root_handle;
-       if(root=="HKCR")
-               root_handle = HKEY_CLASSES_ROOT;
-       else if(root=="HKCC")
-               root_handle = HKEY_CURRENT_CONFIG;
-       else if(root=="HKCU")
-               root_handle = HKEY_CURRENT_USER;
-       else if(root=="HKLM")
-               root_handle = HKEY_LOCAL_MACHINE;
-       else if(root=="HKU")
-               root_handle = HKEY_USERS;
-       else
-               throw invalid_argument("get_registry_value");
-
-       HKEY key;
-       LSTATUS err = RegOpenKeyEx(root_handle, key_path.c_str(), 0, KEY_READ, &key);
-       if(err!=ERROR_SUCCESS)
-               throw Msp::system_error("RegOpenKey", err);
-
-       DWORD value_len;
-       err = RegGetValue(key, 0, value_name.c_str(), RRF_RT_REG_SZ, 0, 0, &value_len);
-       if(err!=ERROR_SUCCESS)
-               throw Msp::system_error("RegGetValue", err);
-
-       char *buffer = new char[value_len];
-       err = RegGetValue(key, 0, value_name.c_str(), RRF_RT_REG_SZ, 0, buffer, &value_len);
-       if(err!=ERROR_SUCCESS)
-       {
-               delete[] buffer;
-               throw Msp::system_error("RegGetValue", err);
-       }
-
-       string result(buffer);
-       delete[] buffer;
-       return result;
-#else
-       (void)path;
-       return string();
-#endif
-}
diff --git a/source/sysutils.h b/source/sysutils.h
deleted file mode 100644 (file)
index 7300869..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef SYSUTILS_H_
-#define SYSUTILS_H_
-
-#include <string>
-#include <msp/fs/path.h>
-
-std::string get_system_type();
-Msp::FS::Path get_program_files_x86_dir();
-
-template<typename T>
-T get_registry_value(const std::string &);
-
-#endif
diff --git a/source/tar.cpp b/source/tar.cpp
deleted file mode 100644 (file)
index 07c14ce..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-#include <cstring>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include "builder.h"
-#include "internaltask.h"
-#include "sourcepackage.h"
-#include "tar.h"
-#include "tarball.h"
-
-using namespace std;
-using namespace Msp;
-
-Tar::Tar(Builder &b):
-       Tool(b, "TAR")
-{
-       processing_unit = COMPONENT;
-       set_run_internal(&_run);
-}
-
-Target *Tar::create_target(const vector<Target *> &sources, const string &arg)
-{
-       if(sources.empty() || !sources.front()->get_package())
-               throw invalid_argument("Tar::create_target");
-
-       TarBall *tarball = new TarBall(builder, *sources.front()->get_package(), arg);
-       for(Target *s: sources)
-               tarball->add_dependency(*s);
-
-       tarball->set_tool(*this);
-
-       return tarball;
-}
-
-bool Tar::_run(const TarBall &tarball)
-{
-       const FS::Path &pkg_src = tarball.get_package()->get_source_directory();
-       FS::Path basedir = FS::basepart(FS::basename(tarball.get_path()));
-
-       IO::File out(tarball.get_path().str(), IO::M_WRITE);
-       for(Target *d: tarball.get_dependencies())
-       {
-               FileTarget *ft = dynamic_cast<FileTarget *>(d);
-               if(!ft)
-                       continue;
-
-               char buf[4096];
-               memset(buf, 0, 512);
-
-               string rel_path = (basedir/relative(ft->get_path(), pkg_src)).str();
-               if(rel_path.size()>99)
-               {
-                       IO::print("Can't store %s in tar archive - too long name\n", rel_path);
-                       return false;
-               }
-
-               memcpy(buf, rel_path.data(), rel_path.size());
-
-               FS::Stat st = FS::stat(ft->get_path());
-               store_number(buf+100, 0666, 7);
-               store_number(buf+108, 0, 7);
-               store_number(buf+116, 0, 7);
-               store_number(buf+124, st.get_size(), 11);
-               store_number(buf+136, st.get_modify_time().to_unixtime(), 11);
-               buf[156] = '0';
-
-               memset(buf+148, ' ', 8);
-               unsigned chk = 0;
-               for(unsigned j=0; j<512; ++j)
-                       chk += static_cast<unsigned char>(buf[j]);
-               store_number(buf+148, chk, 7);
-               buf[155] = 0;
-
-               out.write(buf, 512);
-               IO::File in(ft->get_path().str());
-               for(unsigned j=0; j<st.get_size(); j+=4096)
-               {
-                       unsigned len = in.read(buf, 4096);
-                       len += ((~len)+1)&0777;
-                       out.write(buf, len);
-               }
-       }
-
-       return true;
-}
-
-void Tar::store_number(char *buf, unsigned value, unsigned length)
-{
-       for(unsigned i=length; i--;)
-       {
-               buf[i] = '0'+value%8;
-               value /= 8;
-       }
-}
diff --git a/source/tar.h b/source/tar.h
deleted file mode 100644 (file)
index befebb8..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef TAR_H_
-#define TAR_H_
-
-#include "tool.h"
-
-class TarBall;
-
-class Tar: public Tool
-{
-public:
-       Tar(Builder &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static bool _run(const TarBall &);
-       static void store_number(char *, unsigned, unsigned);
-};
-
-#endif
diff --git a/source/tarball.cpp b/source/tarball.cpp
deleted file mode 100644 (file)
index 7596a3d..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#include "sourcepackage.h"
-#include "tar.h"
-#include "tarball.h"
-
-using namespace std;
-
-TarBall::TarBall(Builder &b, const SourcePackage &p, const string &n):
-       FileTarget(b, p, p.get_source_directory()/(n+".tar"))
-{ }
-
-const SourcePackage *TarBall::get_package() const
-{
-       return static_cast<const SourcePackage *>(package);
-}
diff --git a/source/tarball.h b/source/tarball.h
deleted file mode 100644 (file)
index 105a2b1..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef TARBALL_H_
-#define TARBALL_H_
-
-#include "filetarget.h"
-
-class TarBall: public FileTarget
-{
-public:
-       TarBall(Builder &, const SourcePackage &, const std::string &);
-
-       const char *get_type() const override { return "TarBall"; }
-       const SourcePackage *get_package() const;
-};
-
-#endif
diff --git a/source/target.cpp b/source/target.cpp
deleted file mode 100644 (file)
index dbdd950..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include "builder.h"
-#include "filetarget.h"
-#include "sourcepackage.h"
-#include "target.h"
-#include "task.h"
-#include "tool.h"
-
-using namespace std;
-using namespace Msp;
-
-Target::Target(Builder &b, const string &n):
-       builder(b),
-       name(n)
-{
-       builder.get_build_graph().add_target(this);
-}
-
-void Target::add_dependency(Target &dep)
-{
-       if(&dep==this)
-               throw invalid_argument("Target::add_depend");
-       depends.push_back(&dep);
-       if(state>PREPARING)
-               dep.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
-}
-
-void Target::add_transitive_dependency(Target &dep)
-{
-       if(&dep==this)
-               throw invalid_argument("Target::add_transitive_dependency");
-       trans_depends.push_back(&dep);
-}
-
-void Target::add_side_effect(Target &se)
-{
-       side_effects.push_back(&se);
-       if(tool)
-               se.set_tool(*tool);
-       se.primary_target = this;
-       /* Side effects are checked for rebuild after the primary target.  Recheck
-       the primary if a side effect is marked for rebuild. */
-       se.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
-}
-
-Target *Target::get_buildable_target()
-{
-       if(primary_target)
-               return primary_target->get_buildable_target();
-       if(!needs_rebuild())
-               return 0;
-
-       bool self_ok = state!=BUILDING;
-       for(Target *d: depends)
-       {
-               // Avoid infinite recursion if a target depends on its own side effect
-               if(any_equals(side_effects, d))
-                       continue;
-
-               Target *tgt = d->get_buildable_target();
-               if(tgt)
-                       return tgt;
-               else if(d->needs_rebuild())
-                       self_ok = false;
-       }
-
-       if(self_ok)
-               return this;
-
-       return 0;
-}
-
-void Target::set_tool(Tool &t)
-{
-       tool = &t;
-       for(Target *s: side_effects)
-               s->set_tool(t);
-}
-
-void Target::collect_build_info(BuildInfo &binfo) const
-{
-       if(tool)
-               binfo.update_from(tool->get_build_info());
-       if(component)
-               binfo.update_from(component->get_build_info());
-       else if(package)
-               binfo.update_from(package->get_build_info());
-}
-
-void Target::force_rebuild()
-{
-       if(!is_buildable())
-               throw logic_error("Target::force_rebuild");
-       mark_rebuild("Forced rebuild");
-}
-
-void Target::mark_rebuild(const string &reason)
-{
-       if(reason.empty())
-               throw invalid_argument("No reason given for rebuilding "+name);
-
-       state = REBUILD;
-       rebuild_reason = reason;
-
-       builder.get_logger().log("rebuild", "Rebuilding %s: %s", name, reason);
-
-       signal_bubble_rebuild.emit();
-}
-
-void Target::prepare()
-{
-       if(state>PREPARING)
-               return;
-       if(state==PREPARING)
-       {
-               builder.get_logger().log("problems", "Dependency cycle detected at %s", name);
-               problems.push_back("Dependency cycle detected");
-               state = BROKEN;
-               return;
-       }
-
-       state = PREPARING;
-       /* Prepare existing dependencies early, because their information may be
-       needed to find other dependencies. */
-       for(Target *d: depends)
-               d->prepare();
-       if(tool)
-               tool->prepare();
-
-       find_dependencies();
-       bool broken = !problems.empty();
-
-       if(tool)
-       {
-               if(FileTarget *tool_exe = tool->get_executable())
-                       add_dependency(*tool_exe);
-               broken |= !tool->get_problems().empty();
-
-               // Only check package and component problems for buildable targets
-               // XXX How to propagate nested package problems?
-               broken |= (package && !package->get_problems().empty());
-               broken |= (component && !component->get_problems().empty());
-       }
-
-       /* Now that all dependencies are known, prepare them again.  This will do
-       nothing to already prepared targets. */
-       for(Target *d: depends)
-       {
-               d->prepare();
-               broken |= d->is_broken();
-       }
-       for(Target *d: trans_depends)
-               d->prepare();
-
-       check_rebuild();
-       if(broken)
-               state = BROKEN;
-       else if(state==PREPARING)
-               state = UPTODATE;
-
-       for(Target *d: depends)
-               d->signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
-}
-
-Task *Target::build()
-{
-       if(primary_target)
-               return primary_target->build();
-
-       Task *task = tool->run(*this);
-       task->signal_finished.connect(sigc::mem_fun(this, &Target::build_finished));
-       state = BUILDING;
-
-       build(*task);
-       for(Target *s: side_effects)
-               s->build(*task);
-
-       return task;
-}
-
-void Target::build_finished(bool success)
-{
-       state = UPTODATE;
-       if(success)
-       {
-               modified();
-               for(Target *s: side_effects)
-                       s->build_finished(success);
-               signal_modified.emit();
-       }
-}
diff --git a/source/target.h b/source/target.h
deleted file mode 100644 (file)
index 6ef6457..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-#ifndef TARGET_H_
-#define TARGET_H_
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-#include <sigc++/signal.h>
-#include <msp/time/timestamp.h>
-
-class Builder;
-class BuildInfo;
-class Component;
-class SourcePackage;
-class Task;
-class Tool;
-
-/**
-Targets make up the build graph.  This class is a base for all target types and
-handles many common tasks.  See also FileTarget and VirtualTarget.
-
-Targets can depend on other targets.  There are two kinds of dependencies:
-normal and transitive.  Normal dependencies will need to be built before the
-target itself, and will cause the target to be rebuilt if modified.  Transitive
-dependencies can be used by other targets further down the chain.
-*/
-class Target
-{
-public:
-       using Dependencies = std::vector<Target *>;
-
-protected:
-       enum State
-       {
-               INIT,
-               PREPARING,
-               REBUILD,
-               BUILDING,
-               UPTODATE,
-               BROKEN
-       };
-
-public:
-       sigc::signal<void> signal_bubble_rebuild;
-       sigc::signal<void> signal_modified;
-
-protected:
-       Builder &builder;
-       const SourcePackage *package = 0;
-       const Component *component = 0;
-       std::string name;
-
-       Tool *tool = 0;
-       State state = INIT;
-       std::string rebuild_reason;
-       std::vector<std::string> problems;
-
-       Dependencies depends;
-       Dependencies trans_depends;
-       Dependencies side_effects;
-       Target *primary_target = 0;
-
-       Target(Builder &, const std::string &);
-public:
-       virtual ~Target() { }
-
-       virtual const char *get_type() const = 0;
-       const std::string &get_name() const { return name; }
-       const SourcePackage *get_package() const { return package; }
-       const Component *get_component() const { return component; }
-
-       /** Adds a dependency for the target.  Order is preseved and is important
-       for some target types.  It is an error to create dependency cycles, although
-       this won't be detected until the targets are prepared. */
-       void add_dependency(Target &);
-
-       void add_transitive_dependency(Target &);
-
-       /** Adds a side effect for the target.  Side effects are not built on their
-       own, but together with their primary target. */
-       void add_side_effect(Target &);
-
-protected:
-       /** Finds dependencies for the target.  Called during preparation.  If the
-       target needs to recursively inspect its dependencies, it should prepare its
-       direct dependencies first. */
-       virtual void find_dependencies() { }
-
-public:
-       /// Returns the dependencies of the target, in the order they were added.
-       const Dependencies &get_dependencies() const { return depends; }
-
-       const Dependencies &get_transitive_dependencies() const { return trans_depends; }
-
-       /// Returns the side effects of the target.
-       const Dependencies &get_side_effects() const { return side_effects; }
-
-       /// Returns the primary target associated with a side effect target.
-       Target *get_primary_target() const { return primary_target; }
-
-       /** Tries to locate a target that will help getting this target built.  If
-       all dependencies are up-to-date, returns this target.  If there are no
-       targets ready to be built (maybe because they are being built right now),
-       returns 0. */
-       virtual Target *get_buildable_target();
-
-       /** If this target is a proxy for another (such as InstalledFile), return
-       that target.  Otherwise, return the target itself.  Implementors should call
-       the function recursively to find the final target. */
-       virtual Target *get_real_target() { return this; }
-
-       void set_tool(Tool &);
-
-       /** Returns the tool used to build the target.  To actually build it, call
-       the build() function. */
-       const Tool *get_tool() const { return tool; }
-
-       virtual void collect_build_info(BuildInfo &) const;
-
-       /** Indicates if it's possible to build this target. */
-       bool is_buildable() const { return tool!=0; }
-
-       /** Indicates if this target needs rebuilding.  Only makes sense after the
-       target has been prepared. */
-       bool needs_rebuild() const { return state>PREPARING && state<UPTODATE; }
-
-       /** Returns the reason for rebuilding this target.  Only makes sense after
-       the target has been prepared. */
-       const std::string &get_rebuild_reason() const { return rebuild_reason; }
-
-       /** Forces rebuild of the target. */
-       void force_rebuild();
-
-protected:
-       /** Marks the target to be rebuilt and specified a reason for it. */
-       void mark_rebuild(const std::string &);
-
-       /** Checks if the target needs to be rebuilt and why. */
-       virtual void check_rebuild() = 0;
-
-public:
-       bool is_broken() const { return state==BROKEN; }
-
-       const std::vector<std::string> &get_problems() const { return problems; }
-
-       /** Prepares the target by finding dependencies, recursively preparing them
-       and then checking whether rebuilding is needed. */
-       void prepare();
-
-       /** Invokes the associated Tool to build the target and returns the
-       resulting Task.  The task must be started by the caller. */
-       virtual Task *build();
-
-protected:
-       /** Targets can override this to do additional setup on the Task.  This is
-       also called on side effects, which normally do not get built by themselves. */
-       virtual void build(Task &) { }
-
-       /** Handler for Task::signal_finished. */
-       virtual void build_finished(bool);
-
-       virtual void modified() { }
-
-public:
-       /** Removes any results of building the target. */
-       virtual void clean() { }
-};
-
-#endif
diff --git a/source/task.cpp b/source/task.cpp
deleted file mode 100644 (file)
index e72a741..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#include <msp/fs/dir.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include "task.h"
-
-using namespace std;
-using namespace Msp;
-
-void Task::add_file(const FS::Path &f)
-{
-       files.push_back(f);
-}
-
-void Task::set_unlink(bool u)
-{
-       unlink = u;
-}
-
-void Task::prepare()
-{
-       for(const FS::Path &f: files)
-       {
-               if(FS::exists(f))
-               {
-                       // If the file exists, the directory it's in must exist too
-                       FS::unlink(f);
-               }
-               else
-               {
-                       FS::Path dir = FS::dirname(f);
-                       if(!FS::exists(dir))
-                               FS::mkpath(dir, 0755);
-               }
-       }
-}
diff --git a/source/task.h b/source/task.h
deleted file mode 100644 (file)
index 915ffe1..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef TASK_H_
-#define TASK_H_
-
-#include <string>
-#include <sigc++/signal.h>
-#include <msp/fs/path.h>
-
-/**
-Tasks are used to manage other programs and worker threads involved in the
-build process.  They are run asynchronously by default, but a wait() method is
-provided should it be necessary to wait for a task to finish.
-*/
-class Task
-{
-public:
-       enum Status
-       {
-               RUNNING,
-               SUCCESS,
-               ERROR
-       };
-
-       sigc::signal<void, bool> signal_finished;
-
-protected:
-       std::vector<Msp::FS::Path> files;
-       bool unlink = false;
-
-       Task() = default;
-public:
-       virtual ~Task() { }
-
-       /** Associate the task with a file. */
-       void add_file(const Msp::FS::Path &);
-
-       /** If set to true, the associated files are removed before the task is
-       started. */
-       void set_unlink(bool = true);
-
-       /** Returns the command being executed for this task.  Only makes sense if
-       an external command is involved. */
-       virtual std::string get_command() const = 0;
-
-       /// Starts the task.
-       virtual void start() = 0;
-
-protected:
-       /// Ensures that the output directory exists and removes files if necessary.
-       void prepare();
-
-public:
-       /// Checks the status of the task and immediately returns.
-       virtual Status check() = 0;
-
-       /// Waits for the task to finish and returns its final status.
-       virtual Status wait() = 0;
-};
-
-#endif
diff --git a/source/templatefile.h b/source/templatefile.h
deleted file mode 100644 (file)
index 0912025..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef TEMPLATEFILE_H_
-#define TEMPLATEFILE_H_
-
-#include "sourcefile.h"
-
-/**
-Input file for SourceGenerator.
-*/
-class TemplateFile: public SourceFile
-{
-public:
-       TemplateFile(Builder &b, const Component &c, const Msp::FS::Path &p): SourceFile(b, c, p) { }
-
-       const char *get_type() const override { return "TemplateFile"; }
-};
-
-#endif
diff --git a/source/tool.cpp b/source/tool.cpp
deleted file mode 100644 (file)
index eaf4977..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/fs/utils.h>
-#include <msp/strings/format.h>
-#include "architecture.h"
-#include "builder.h"
-#include "filetarget.h"
-#include "tool.h"
-
-using namespace std;
-using namespace Msp;
-
-void Tool::set_command(const string &cmd, bool cross)
-{
-       if(cmd.empty())
-               throw invalid_argument("Tool::set_command");
-
-       if(cross && architecture->is_cross() && !FS::Path(cmd).is_absolute())
-               command = format("%s-%s", architecture->get_cross_prefix(), cmd);
-       else
-               command = cmd;
-}
-
-void Tool::set_run(function<Task *(const Target &)> f)
-{
-       run_func = move(f);
-}
-
-bool Tool::accepts_suffix(const string &suffix, bool aux) const
-{
-       return (any_equals(input_suffixes, suffix) || (aux && any_equals(aux_suffixes, suffix)));
-}
-
-Target *Tool::create_target(Target &source, const string &arg)
-{
-       vector<Target *> sources;
-       sources.push_back(&source);
-       return create_target(sources, arg);
-}
-
-void Tool::prepare()
-{
-       if(prepared)
-               return;
-
-       prepared = true;
-
-       if(!command.empty())
-               executable = builder.get_vfs().find_binary(command);
-       prepare(*this);
-       if(!command.empty() && !executable)
-       {
-               builder.get_logger().log("problems", "Can't find executable %s for %s", command, tag);
-               problems.push_back(format("Can't find executable %s", command));
-       }
-}
-
-void Tool::prepare(Tool &tool) const
-{
-       if(&tool!=this && tool.get_base_tool()!=this)
-               throw invalid_argument("Tool::prepare");
-
-       if(&tool!=this && !command.empty() && tool.command.empty())
-               throw logic_error("Derived tool has no command");
-
-       do_prepare(tool);
-}
-
-string Tool::create_build_signature(const BuildInfo &) const
-{
-       if(executable)
-               return format("%s=%s", tag, FS::basename(executable->get_path()));
-       else
-               return string();
-}
-
-
-void operator>>(const LexicalConverter &conv, Tool::ProcessingUnit &unit)
-{
-       const string &str = conv.get();
-       if(str=="FILE")
-               unit = Tool::ONE_FILE;
-       else if(str=="DIRECTORY")
-               unit = Tool::DIRECTORY;
-       else if(str=="COMPONENT")
-               unit = Tool::COMPONENT;
-       else
-               throw lexical_error(format("conversion of '%s' to ProcessingUnit", str));
-}
diff --git a/source/tool.h b/source/tool.h
deleted file mode 100644 (file)
index b756563..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-#ifndef TOOL_H_
-#define TOOL_H_
-
-#include <functional>
-#include <string>
-#include <vector>
-#include <msp/fs/path.h>
-#include "buildinfo.h"
-#include "externaltask.h"
-#include "internaltask.h"
-#include "sourcepackage.h"
-#include "target.h"
-#include "virtualfilesystem.h"
-
-class Architecture;
-class Builder;
-class BuildInfo;
-class Component;
-class FileTarget;
-
-class ToolData
-{
-public:
-       VirtualFileSystem::SearchPath system_path;
-       BuildInfo build_info;
-       Msp::Variant extra_data;
-       std::vector<std::string> problems;
-};
-
-/**
-Base class for tools.  Tools are used to turn targets into other targets.
-Examples include compilers and linkers.
-*/
-class Tool: protected ToolData
-{
-public:
-       enum ProcessingUnit
-       {
-               ONE_FILE,
-               DIRECTORY,
-               COMPONENT
-       };
-
-protected:
-       Builder &builder;
-       const Architecture *architecture = 0;
-       std::string tag;
-       std::string command;
-       FileTarget *executable = 0;
-       std::vector<std::string> input_suffixes;
-       std::vector<std::string> aux_suffixes;
-       ProcessingUnit processing_unit = ONE_FILE;
-       std::function<Task *(const Target &)> run_func;
-       bool prepared = false;
-
-       Tool(Builder &b, const std::string &t): Tool(b, 0, t) { }
-       Tool(Builder &b, const Architecture *a, const std::string &t): builder(b), architecture(a), tag(t) { }
-
-public:
-       virtual ~Tool() { }
-
-       Builder &get_builder() const { return builder; }
-
-       const std::string &get_tag() const { return tag; }
-
-       /** Returns the architecture this tool builds for.  May return null if the
-       tool is architecture-agnostic. */
-       const Architecture *get_architecture() const { return architecture; }
-
-       virtual const Tool *get_base_tool() const { return this; }
-
-protected:
-       void set_run(std::function<Task *(const Target &)>);
-
-       template<typename T>
-       void set_run_external(ExternalTask::Arguments (*)(const T &, Msp::FS::Path &));
-
-       template<typename T>
-       void set_run_internal(bool (*)(const T &));
-
-public:
-       /** Overrides the command used by the tool.  The new command should accept
-       the same command line arguments.  Only works on tools that use an external
-       command.  If cross is true and the architecture is not native, a cross
-       prefix is added to the command.  May have no effect after prepare() has been
-       called. */
-       void set_command(const std::string &cmd, bool cross = false);
-
-       /** Returns a target for the tool's own executable.  If the tool does not
-       use an external program, returns null.  The tool must be prepared first. */
-       FileTarget *get_executable() const { return executable; }
-
-       /// Returns a list of suffixes that can be processed with this tool.
-       const std::vector<std::string> &get_input_suffixes() const { return input_suffixes; }
-
-       /** Returns a list of suffixes that are associated with this tool, but can't
-       be processed directly.  For example C and C++ headers. */
-       const std::vector<std::string> &get_auxiliary_suffixes() const { return aux_suffixes; }
-
-       /** Indicates whether the tool can accept a suffix.  If aux is true,
-       auxiliary suffixes are considered as well */
-       bool accepts_suffix(const std::string &, bool aux = false) const;
-
-       /** Returns the grouping unit this tool prefers to process. */
-       ProcessingUnit get_processing_unit() const { return processing_unit; }
-
-       /// Returns the systemwide search path for source files.
-       const VirtualFileSystem::SearchPath &get_system_path() const { return system_path; }
-
-       /** Returns tool-specific build info.  This can be used by other tools down
-       the chain. */
-       const BuildInfo &get_build_info() const { return build_info; }
-
-       const Msp::Variant &get_extra_data() const { return extra_data; }
-
-       /// Creates a source file appropriate for this tool.
-       virtual Target *create_source(const Component &, const Msp::FS::Path &) const { return 0; }
-
-       /** Creates a package-less source file appropriate for this tool.  This is
-       called during dependency discovery when no package has created a target for
-       the file. */
-       virtual Target *create_source(const Msp::FS::Path &) const { return 0; }
-
-       /// Convenience function to create a target from a single source.
-       Target *create_target(Target &, const std::string & = std::string());
-
-       /** Creates a target from sources.  The exact types of accepted sources
-       depends on the tool.  The optional second argument can be used to select an
-       alternative target type for tools that can create multiple kinds of targets. */ 
-       virtual Target *create_target(const std::vector<Target *> &, const std::string & = std::string()) = 0;
-
-       /** Creates an install target for a target created by this tool.  Can return
-       null if the tool does not want to handle installing in a special way. */
-       virtual Target *create_install(Target &) const { return 0; }
-
-       virtual std::string create_build_signature(const BuildInfo &) const;
-
-       void prepare();
-       void prepare(Tool &) const;
-
-protected:
-       virtual void do_prepare(ToolData &) const { }
-
-public:
-       const std::vector<std::string> &get_problems() const { return problems; }
-
-       /** Invokes the tool to build a target.  This should not be called directly;
-       use Target::build() instead. */
-       Task *run(const Target &t) const { return run_func(t); }
-};
-
-
-template<typename T>
-void Tool::set_run_external(ExternalTask::Arguments (*f)(const T &, Msp::FS::Path &))
-{
-       set_run([f](const Target &t){
-               Msp::FS::Path work_dir = t.get_package()->get_source_directory();
-               ExternalTask::Arguments args = f(dynamic_cast<const T &>(t), work_dir);
-               return new ExternalTask(args, work_dir);
-       });
-}
-
-template<typename T>
-void Tool::set_run_internal(bool (*f)(const T &))
-{
-       set_run([f](const Target &t){
-               const T &ct = dynamic_cast<const T &>(t);
-               return new InternalTask([f, &ct]{ return f(ct); });
-       });
-}
-
-
-void operator>>(const Msp::LexicalConverter &, Tool::ProcessingUnit &);
-
-#endif
diff --git a/source/toolchain.cpp b/source/toolchain.cpp
deleted file mode 100644 (file)
index c5b8caa..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/core/maputils.h>
-#include "tool.h"
-#include "toolchain.h"
-
-using namespace std;
-using namespace Msp;
-
-Toolchain::~Toolchain()
-{
-       for(const auto &kvp: tools)
-               delete kvp.second;
-       for(Toolchain *c: chains)
-               delete c;
-}
-
-void Toolchain::add_tool(Tool *tool)
-{
-       insert_unique(tools, tool->get_tag(), tool);
-}
-
-void Toolchain::add_toolchain(Toolchain *chain)
-{
-       auto i = upper_bound(chains, chain->get_priority(), [](int p, Toolchain *tc){ return p>tc->get_priority(); });
-       chains.insert(i, chain);
-}
-
-bool Toolchain::has_tool(const string &tag) const
-{
-       if(tools.count(tag))
-               return true;
-       return any_of(chains.begin(), chains.end(), [&tag](Toolchain *tc){ return tc->has_tool(tag); });
-}
-
-Tool &Toolchain::get_tool(const string &tag) const
-{
-       if(!tools.count(tag))
-       {
-               for(const Toolchain *c: chains)
-                       if(c->has_tool(tag))
-                               return c->get_tool(tag);
-       }
-
-       return *get_item(tools, tag);
-}
-
-Tool *Toolchain::get_tool_for_suffix(const string &suffix, bool aux) const
-{
-       for(const auto &kvp: tools)
-               if(kvp.second->accepts_suffix(suffix, aux))
-                       return kvp.second;
-
-       for(const Toolchain *c: chains)
-               if(Tool *tool = c->get_tool_for_suffix(suffix, aux))
-                       return tool;
-
-       return 0;
-}
diff --git a/source/toolchain.h b/source/toolchain.h
deleted file mode 100644 (file)
index 34fc0a2..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef TOOLCHAIN_H_
-#define TOOLCHAIN_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-class Tool;
-
-/**
-A container for tools.  Performs lookup based on tag or filename extension.
-*/
-class Toolchain
-{
-private:
-       std::string name;
-       int priority = 0;
-       std::map<std::string, Tool *> tools;
-       std::vector<Toolchain *> chains;
-
-protected:
-       Toolchain(const std::string &n, unsigned p): name(n), priority(p) { }
-public:
-       Toolchain() = default;
-       ~Toolchain();
-
-       const std::string &get_name() const { return name; }
-       int get_priority() const { return priority; }
-       void add_tool(Tool *);
-       void add_toolchain(Toolchain *);
-       bool has_tool(const std::string &) const;
-       Tool &get_tool(const std::string &) const;
-       Tool *get_tool_for_suffix(const std::string &, bool = false) const;
-       const std::vector<Toolchain *> &get_toolchains() { return chains; }
-};
-
-#endif
diff --git a/source/vcxprojectfile.cpp b/source/vcxprojectfile.cpp
deleted file mode 100644 (file)
index c591524..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <msp/crypto/md5.h>
-#include <msp/strings/format.h>
-#include "builder.h"
-#include "sourcepackage.h"
-#include "vcxprojectfile.h"
-
-using namespace Msp;
-
-VcxProjectFile::VcxProjectFile(Builder &b, const SourcePackage &p):
-       FileTarget(b, p, p.get_source_directory()/(p.get_name()+".vcxproj"))
-{
-       tool = &builder.get_toolchain().get_tool("VCXG");
-
-       char digest[16];
-       Crypto::MD5(package->get_name()).get_digest(digest, sizeof(digest));
-       digest[6] = 3;
-       digest[8] = (digest[6]&0x3F)|0x80;
-       for(unsigned j=0; j<sizeof(digest); ++j)
-       {
-               if(j==4 || j==6 || j==8 || j==10)
-                       guid += '-';
-               guid += format("%02x", static_cast<unsigned char>(digest[j]));
-       }
-}
diff --git a/source/vcxprojectfile.h b/source/vcxprojectfile.h
deleted file mode 100644 (file)
index 268d4bb..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef VCXPROJECTFILE_H_
-#define VCXPROJECTFILE_H_
-
-#include "filetarget.h"
-
-class VcxProjectFile: public FileTarget
-{
-private:
-       std::string guid;
-
-public:
-       VcxProjectFile(Builder &, const SourcePackage &);
-
-       const char *get_type() const override { return "VcxProjectFile"; }
-
-       const std::string &get_guid() const { return guid; }
-};
-
-#endif
diff --git a/source/vcxprojectgenerator.cpp b/source/vcxprojectgenerator.cpp
deleted file mode 100644 (file)
index 8cfc12a..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-#include <msp/core/application.h>
-#include <msp/fs/utils.h>
-#include <msp/io/print.h>
-#include <msp/strings/utils.h>
-#include "builder.h"
-#include "csourcefile.h"
-#include "executable.h"
-#include "internaltask.h"
-#include "sourcepackage.h"
-#include "vcxprojectfile.h"
-#include "vcxprojectgenerator.h"
-
-using namespace std;
-using namespace Msp;
-
-VcxProjectGenerator::VcxProjectGenerator(Builder &b):
-       Tool(b, "VCXG")
-{
-       set_run_internal(_run);
-}
-
-Target *VcxProjectGenerator::create_target(const vector<Target *> &, const string &)
-{
-       throw logic_error("Not implemented");
-}
-
-bool VcxProjectGenerator::_run(const VcxProjectFile &project)
-{
-       const SourcePackage &spkg = *project.get_package();
-       Builder &builder = spkg.get_builder();
-
-       IO::BufferedFile out(project.get_path().str(), IO::M_WRITE);
-       IO::print(out, "<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n");
-
-       IO::print(out, "\t<ItemGroup Label=\"ProjectConfigurations\">\n");
-       vector<string> build_types = builder.get_build_types();
-       const char *platforms[] = { "Win32", "x64" };
-       for(const char *p: platforms)
-               for(const string &b: build_types)
-               {
-                       IO::print(out, "\t\t<ProjectConfiguration Include=\"%s|%s\">\n", b, p);
-                       IO::print(out, "\t\t\t<Configuration>%s</Configuration>\n", b);
-                       IO::print(out, "\t\t\t<Platform>%s</Platform>\n", p);
-                       IO::print(out, "\t\t</ProjectConfiguration>\n");
-               }
-       IO::print(out, "\t</ItemGroup>\n");
-
-       IO::print(out, "\t<PropertyGroup Label=\"Globals\">\n");
-       IO::print(out, "\t\t<VCProjectVersion>15.0</VCProjectVersion>\n");
-       IO::print(out, "\t\t<Keyword>MakeFileProj</Keyword>\n");
-       IO::print(out, "\t\t<ProjectGuid>{%s}</ProjectGuid>\n", project.get_guid());
-       IO::print(out, "\t</PropertyGroup>\n");
-
-       IO::print(out, "\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n");
-
-       const Executable *exe = 0;
-       for(const Target *t: builder.get_build_graph().get_target("world")->get_dependencies())
-               if(t->get_package()==&spkg)
-                       if((exe = dynamic_cast<const Executable *>(t)))
-                               break;
-
-       const char *argv0 = Application::get_argv0();
-       const string &toolchain = builder.get_current_arch().get_toolchain();
-       for(const char *p: platforms)
-               for(const string &b: build_types)
-               {
-                       string base_cmd = format("%s --arch=%s-%s --build-type=%s --prefix=%s", argv0, p, toolchain, b, builder.get_prefix());
-                       IO::print(out, "\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='%s|%s'\" Label=\"Configuration\">\n", b, p);
-                       IO::print(out, "\t\t<ConfigurationType>MakeFile</ConfigurationType>\n");
-                       IO::print(out, "\t\t<NMakeBuildCommandLine>%s</NMakeBuildCommandLine>\n", base_cmd);
-                       IO::print(out, "\t\t<NMakeCleanCommandLine>%s -c</NMakeCleanCommandLine>\n", base_cmd);
-                       IO::print(out, "\t\t<NMakeReBuildCommandLine>%s -B</NMakeReBuildCommandLine>\n", base_cmd);
-                       if(exe)
-                               IO::print(out, "\t\t<NMakeOutput>%s</NMakeOutput>\n", exe->get_path());
-                       IO::print(out, "\t</PropertyGroup>\n");
-               }
-
-       IO::print(out, "\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n");
-
-       vector<const FileTarget *> sources;
-       vector<const FileTarget *> includes;
-       vector<const FileTarget *> others;
-       BuildInfo build_info;
-       for(const auto &kvp: builder.get_build_graph().get_targets())
-               if(kvp.second->get_package()==&spkg)
-               {
-                       if(kvp.second->is_buildable())
-                       {
-                               BuildInfo tgt_binfo;
-                               kvp.second->collect_build_info(tgt_binfo);
-                               build_info.update_from(tgt_binfo, BuildInfo::CHAINED);
-                       }
-                       else if(const FileTarget *file = dynamic_cast<const FileTarget *>(kvp.second))
-                       {
-                               if(dynamic_cast<const CSourceFile *>(file))
-                               {
-                                       string ext = tolower(FS::extpart(FS::basename(file->get_path())));
-                                       if(ext==".h" || ext==".hpp")
-                                               includes.push_back(file);
-                                       else
-                                               sources.push_back(file);
-                               }
-                               else
-                                       others.push_back(file);
-                       }
-               }
-
-       if(!build_info.incpath.empty())
-       {
-               IO::print(out, "\t<PropertyGroup>\n");
-               string path_str;
-               for(const FS::Path &p: build_info.incpath)
-                       append(path_str, ";", p.str());
-               IO::print(out, "\t\t<NMakeIncludeSearchPath>%s</NMakeIncludeSearchPath>\n", path_str);
-               IO::print(out, "\t</PropertyGroup>\n");
-       }
-
-       IO::print(out, "\t<ItemGroup>\n");
-       for(const FileTarget *s: sources)
-               IO::print(out, "\t\t<ClCompile Include=\"%s\" />\n", s->get_path());
-       IO::print(out, "\t</ItemGroup>\n");
-
-       IO::print(out, "\t<ItemGroup>\n");
-       for(const FileTarget *i: includes)
-               IO::print(out, "\t\t<ClInclude Include=\"%s\" />\n", i->get_path());
-       IO::print(out, "\t</ItemGroup>\n");
-
-       IO::print(out, "\t<ItemGroup>\n");
-       for(const FileTarget *t: others)
-               IO::print(out, "\t\t<None Include=\"%s\" />\n", t->get_path());
-       IO::print(out, "\t</ItemGroup>\n");
-
-       IO::print(out, "\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n");
-       IO::print(out, "</Project>\n");
-
-       return true;
-}
diff --git a/source/vcxprojectgenerator.h b/source/vcxprojectgenerator.h
deleted file mode 100644 (file)
index f8283ff..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef VCXPROJECTGENERATOR_H_
-#define VCXPROJECTGENERATOR_H_
-
-#include "tool.h"
-
-class VcxProjectFile;
-
-class VcxProjectGenerator: public Tool
-{
-public:
-       VcxProjectGenerator(Builder &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static bool _run(const VcxProjectFile &);
-};
-
-#endif
diff --git a/source/virtualfilesystem.cpp b/source/virtualfilesystem.cpp
deleted file mode 100644 (file)
index 16d4de8..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-#include <msp/core/environ.h>
-#include <msp/fs/stat.h>
-#include <msp/fs/utils.h>
-#include <msp/io/print.h>
-#include <msp/strings/utils.h>
-#include "builder.h"
-#include "csourcefile.h"
-#include "executable.h"
-#include "importlibrary.h"
-#include "sharedlibrary.h"
-#include "staticlibrary.h"
-#include "tool.h"
-#include "virtualfilesystem.h"
-
-using namespace std;
-using namespace Msp;
-
-FileTarget *VirtualFileSystem::get_target(const FS::Path &p) const
-{
-       auto i = targets.find(p.str());
-       if(i!=targets.end())
-               return static_cast<FileTarget *>(i->second);
-       return 0;
-}
-
-void VirtualFileSystem::register_path(const FS::Path &path, FileTarget *t)
-{
-       targets.insert({ path, t });
-       nonexistent.erase(path);
-       builder.get_logger().log("vfs", "Path %s registered to %s", path, t->get_name());
-}
-
-FileTarget *VirtualFileSystem::find_header(const string &name, Tool *tool, const SearchPath &path, bool use_syspath)
-{
-       if(!tool)
-               tool = builder.get_toolchain().get_tool_for_suffix(FS::extpart(FS::basename(name)), true);
-       if(!tool)
-               return 0;
-
-       tool->prepare();
-
-       SearchPath combined_path = path;
-       if(use_syspath)
-       {
-               const SearchPath &syspath = tool->get_system_path();
-               combined_path.insert(combined_path.end(), syspath.begin(), syspath.end());
-       }
-
-       for(const FS::Path &p: combined_path)
-       {
-               FS::Path filename = p/name;
-               if(FileTarget *tgt = get_target(filename))
-               {
-                       builder.get_logger().log("vfs", "Header %s found in %s as existing %s", name, p.str(), tgt->get_type());
-                       return tgt;
-               }
-               else if(file_exists(filename))
-               {
-                       builder.get_logger().log("vfs", "Header %s found in %s", name, p.str());
-                       return dynamic_cast<FileTarget *>(tool->create_source(filename));
-               }
-
-               builder.get_logger().log("vfs", "Header %s not found in %s", name, p.str());
-       }
-
-       return 0;
-}
-
-FileTarget *VirtualFileSystem::find_library(const string &lib, const SearchPath &path, BuildInfo::LibraryMode mode, bool use_syspath)
-{
-       SearchPath combined_path = path;
-       if(use_syspath)
-       {
-               Tool &linker = builder.get_toolchain().get_tool("LINK");
-               linker.prepare();
-               const SearchPath &syspath = linker.get_system_path();
-               combined_path.insert(combined_path.end(), syspath.begin(), syspath.end());
-       }
-
-       const Architecture &arch = builder.get_current_arch();
-
-       vector<string> shared_names;
-       bool use_import_lib = false;
-       if(mode!=BuildInfo::FORCE_STATIC)
-       {
-               shared_names = Pattern::apply_list(arch.get_patterns<ImportLibrary>(), lib);
-               if(!(use_import_lib = !shared_names.empty()))
-                       shared_names = Pattern::apply_list(arch.get_patterns<SharedLibrary>(), lib);
-       }
-
-       vector<string> static_names;
-       if(mode!=BuildInfo::FORCE_DYNAMIC)
-               static_names = Pattern::apply_list(arch.get_patterns<StaticLibrary>(), lib);
-
-       for(const FS::Path &p: combined_path)
-       {
-               const vector<string> *cur_names = (mode>=BuildInfo::DYNAMIC ? &shared_names : &static_names);
-               for(auto j=cur_names->begin(); j!=cur_names->end(); )
-               {
-                       FS::Path filename = p / *j;
-                       if(FileTarget *tgt = get_target(filename))
-                       {
-                               builder.get_logger().log("vfs", "Library %s (%s) found in %s as existing %s", lib, *j, p.str(), tgt->get_type());
-                               return tgt;
-                       }
-                       else if(file_exists(filename))
-                       {
-                               builder.get_logger().log("vfs", "Library %s (%s) found in %s", lib, *j, p.str());
-                               if(cur_names==&shared_names)
-                               {
-                                       if(use_import_lib)
-                                               return new ImportLibrary(builder, filename);
-                                       return new SharedLibrary(builder, filename);
-                               }
-                               else
-                                       return new StaticLibrary(builder, filename);
-                       }
-
-                       if(++j==cur_names->end())
-                       {
-                               if(mode==BuildInfo::DYNAMIC && cur_names==&shared_names)
-                                       cur_names = &static_names;
-                               else if(mode==BuildInfo::STATIC && cur_names==&static_names)
-                                       cur_names = &shared_names;
-                               else
-                                       break;
-                               j = cur_names->begin();
-                       }
-               }
-
-               builder.get_logger().log("vfs", "Library %s not found in %s", lib, p.str());
-       }
-
-       return 0;
-}
-
-FileTarget *VirtualFileSystem::find_binary(const string &name)
-{
-       SearchPath path;
-       if(FS::Path(name).is_absolute())
-               path.push_back("/");
-       else
-       {
-               if(sys_bin_path.empty())
-               {
-                       string env_path = Msp::getenv("PATH");
-                       if(!env_path.empty())
-                       {
-                               for(const string &p: split(env_path, ':'))
-                                       sys_bin_path.push_back(p);
-                       }
-                       else
-                       {
-                               sys_bin_path.push_back("/bin");
-                               sys_bin_path.push_back("/usr/bin");
-                       }
-               }
-               path = sys_bin_path;
-       }
-
-       for(const FS::Path &p: path)
-       {
-               FS::Path filename = p/name;
-               if(FileTarget *tgt = get_target(filename))
-               {
-                       builder.get_logger().log("vfs", "Binary %s found in %s as existing %s", name, p, tgt->get_type());
-                       return tgt;
-               }
-               else if(file_exists(filename))
-               {
-                       builder.get_logger().log("vfs", "Binary %s found in %s", name, p);
-                       return new Executable(builder, filename);
-               }
-
-               builder.get_logger().log("vfs", "Binary %s not found in %s", name, p);
-       }
-
-       return 0;
-}
-
-bool VirtualFileSystem::file_exists(const FS::Path &filename)
-{
-       if(nonexistent.count(filename))
-               return false;
-       if(FS::is_reg(filename))
-               return true;
-       nonexistent.insert(filename);
-       return false;
-}
diff --git a/source/virtualfilesystem.h b/source/virtualfilesystem.h
deleted file mode 100644 (file)
index 60e198a..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef VIRTUALFILESYSTEM_H_
-#define VIRTUALFILESYSTEM_H_
-
-#include <map>
-#include <set>
-#include <vector>
-#include <msp/fs/path.h>
-#include "buildinfo.h"
-
-class Builder;
-class FileTarget;
-class Pattern;
-class Tool;
-
-/**
-Provides access to the filesystem in a way that takes known targets into
-account.  Thus, targets may be returned for files that do not exist yet if it's
-possible to build them.
-*/
-class VirtualFileSystem
-{
-public:
-       using SearchPath = std::vector<Msp::FS::Path>;
-
-private:
-       Builder &builder;
-       std::map<Msp::FS::Path, FileTarget *> targets;
-       std::set<Msp::FS::Path> nonexistent;
-       SearchPath sys_bin_path;
-
-public:
-       VirtualFileSystem(Builder &b): builder(b) { }
-
-       /** Gets an existing target associated with a path.  If no target has claimed
-       that path, 0 is returned. */
-       FileTarget *get_target(const Msp::FS::Path &) const;
-
-       /** Registers a target with the VFS.  A target may be registered at multiple
-       paths if building it results in multiple files. */
-       void register_path(const Msp::FS::Path &, FileTarget *);
-
-       /** Locates a source file.  If a file is found but no target is associated
-       with it, a new package-less target is created with the appropriate tool.  If
-       use_syspath is true, the system path reported by the tool is also searched. */
-       FileTarget *find_header(const std::string &, Tool *, const SearchPath &, bool use_syspath = true);
-
-       /** Locates a library.  The library name should be the same as what would be
-       used in linking with the library.  If a file is found but no target is
-       associated with it, a new package-less target of appropriate type is
-       created.  If use_syspath is true, the system path reported by the LINK tool
-       is also searched. */
-       FileTarget *find_library(const std::string &, const SearchPath &, BuildInfo::LibraryMode, bool use_syspath = true);
-
-       /** Locates a binary.  The normal search path for binaries is used (usually
-       this means the PATH environment variable).  If a file is found but no target
-       is associated with it, a new package-less Executable target is created. */
-       FileTarget *find_binary(const std::string &);
-
-private:
-       bool file_exists(const Msp::FS::Path &);
-};
-
-#endif
diff --git a/source/virtualtarget.cpp b/source/virtualtarget.cpp
deleted file mode 100644 (file)
index 07177d8..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/fs/path.h>
-#include <msp/fs/utils.h>
-#include "builder.h"
-#include "virtualtarget.h"
-
-using namespace std;
-using namespace Msp;
-
-void VirtualTarget::check_rebuild()
-{
-       // Virtual targets are only rebuilt if their dependencies need rebuilding.
-       auto i = find_if(depends, [](Target *d){ return d->needs_rebuild(); });
-       if(i!=depends.end())
-               mark_rebuild((*i)->get_name()+" needs rebuilding");
-}
-
-Task *VirtualTarget::build()
-{
-       state = UPTODATE;
-       return 0;
-}
diff --git a/source/virtualtarget.h b/source/virtualtarget.h
deleted file mode 100644 (file)
index 5151826..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef VIRTUALTARGET_H_
-#define VIRTUALTARGET_H_
-
-#include "target.h"
-
-/**
-A target that is not associated with any file.
-*/
-class VirtualTarget: public Target
-{
-public:
-       VirtualTarget(Builder &b, const std::string &n): Target(b, n) { }
-
-       const char *get_type() const override { return "VirtualTarget"; }
-private:
-       void check_rebuild() override;
-
-public:
-       Task *build() override;
-};
-
-#endif
diff --git a/source/vssolutionfile.cpp b/source/vssolutionfile.cpp
deleted file mode 100644 (file)
index af7e01f..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#include <msp/core/algorithm.h>
-#include "builder.h"
-#include "sourcepackage.h"
-#include "vssolutionfile.h"
-
-using namespace std;
-using namespace Msp;
-
-VsSolutionFile::VsSolutionFile(Builder &b, const SourcePackage &p):
-       FileTarget(b, p, p.get_source_directory()/(p.get_name()+".sln"))
-{
-       tool = &builder.get_toolchain().get_tool("VSSG");
-}
-
-void VsSolutionFile::find_dependencies()
-{
-       if(FileTarget *project = builder.get_vfs().get_target(package->get_source_directory()/(package->get_name()+".vcxproj")))
-               add_dependency(*project);
-
-       Package::Requirements reqs = package->get_required_packages();
-       for(auto i=reqs.begin(); i!=reqs.end(); ++i)
-               if(const SourcePackage *spkg = dynamic_cast<const SourcePackage *>(*i))
-               {
-                       if(FileTarget *project = builder.get_vfs().get_target(spkg->get_source_directory()/(spkg->get_name()+".vcxproj")))
-                               add_dependency(*project);
-
-                       for(Package *r: spkg->get_required_packages())
-                               if(!any_equals(reqs, r))
-                                       reqs.push_back(r);
-               }
-}
diff --git a/source/vssolutionfile.h b/source/vssolutionfile.h
deleted file mode 100644 (file)
index 0a97d32..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef VSSOLUTIONFILE_H_
-#define VSSOLUTIONFILE_H_
-
-#include "filetarget.h"
-
-class VsSolutionFile: public FileTarget
-{
-public:
-       VsSolutionFile(Builder &, const SourcePackage &);
-
-       const char *get_type() const override { return "VsSolutionFile"; }
-protected:
-       void find_dependencies() override;
-};
-
-#endif
diff --git a/source/vssolutiongenerator.cpp b/source/vssolutiongenerator.cpp
deleted file mode 100644 (file)
index 7dce076..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#include <cstring>
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include "builder.h"
-#include "internaltask.h"
-#include "sourcepackage.h"
-#include "vcxprojectfile.h"
-#include "vssolutionfile.h"
-#include "vssolutiongenerator.h"
-
-using namespace std;
-using namespace Msp;
-
-VsSolutionGenerator::VsSolutionGenerator(Builder &b):
-       Tool(b, "VSSG")
-{
-       set_run_internal(_run);
-}
-
-Target *VsSolutionGenerator::create_target(const vector<Target *> &, const string &)
-{
-       throw logic_error("Not implemented");
-}
-
-bool VsSolutionGenerator::_run(const VsSolutionFile &solution)
-{
-       const SourcePackage &spkg = *solution.get_package();
-       Builder &builder = spkg.get_builder();
-
-       IO::BufferedFile out(solution.get_path().str(), IO::M_WRITE);
-       IO::print(out, "Microsoft Visual Studio Solution File, Format Version 12.00\n");
-       IO::print(out, "MinimumVisualStudioVersion = 10.0.40219.1\n");
-
-       vector<const VcxProjectFile *> projects;
-       for(const Target *t: solution.get_dependencies())
-               if(const VcxProjectFile *project = dynamic_cast<const VcxProjectFile *>(t))
-                       projects.push_back(project);
-
-       for(const VcxProjectFile *p: projects)
-       {
-               const SourcePackage *pkg = p->get_package();
-               IO::print(out, "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"%s\", \"%s\", \"{%s}\"\nEndProject\n",
-                       pkg->get_name(), p->get_path(), p->get_guid());
-       }
-
-       vector<string> build_types = builder.get_build_types();
-       const char *platforms[] = { "x86", "x64" };
-
-       IO::print(out, "Global\n");
-       IO::print(out, "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n");
-       for(const string &t: build_types)
-               for(const char *p: platforms)
-                       IO::print(out, "\t\t%s|%s = %s|%s\n", t, p, t, p);
-       IO::print(out, "\tEndGlobalSection\n");
-       IO::print(out, "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n");
-       for(const VcxProjectFile *p: projects)
-               for(const string &t: build_types)
-                       for(const char *f: platforms)
-                       {
-                               const char *project_platform = (!strcmp(f, "x86") ? "Win32" : f);
-                               IO::print(out, "\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n", p->get_guid(), t, f, t, project_platform);
-                               if(p->get_package()==&spkg)
-                                       IO::print(out, "\t\t{%s}.%s|%s.Build.0 = %s|%s\n", p->get_guid(), t, f, t, project_platform);
-                       }
-       IO::print(out, "\tEndGlobalSection\n");
-       IO::print(out, "EndGlobal\n");
-
-       return true;
-}
diff --git a/source/vssolutiongenerator.h b/source/vssolutiongenerator.h
deleted file mode 100644 (file)
index ca33237..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef VSSOLUTIONGENERATOR_H_
-#define VSSOLUTIONGENERATOR_H_
-
-#include "tool.h"
-
-class VsSolutionFile;
-
-class VsSolutionGenerator: public Tool
-{
-public:
-       VsSolutionGenerator(Builder &);
-
-       Target *create_target(const std::vector<Target *> &, const std::string &) override;
-
-private:
-       static bool _run(const VsSolutionFile &);
-};
-
-#endif