/.config
/builder
+/builder.pc
/builder-stage1
+/libbuilder.a
+/libbuilder.so
+/libandroidtools.a
+/libbuiltintools.a
+/libclangtools.a
+/libdatatools.a
+/libgnutools.a
+/libmsvctools.a
/temp
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
};
};
install true;
+ install_map
+ {
+ map "source/lib" "include/msp/builder";
+ };
+ };
+
+ program "builder"
+ {
+ source "source/cli";
+ use "builder";
+ install true;
};
source_archive
--- /dev/null
+#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 © = 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);
+}
--- /dev/null
+#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
--- /dev/null
+#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());
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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");
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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));
+}
--- /dev/null
+#ifndef BUILTINTOOLS_H_
+#define BUILTINTOOLS_H_
+
+#include <msp/builder/toolchain.h>
+
+class Builder;
+
+class BuiltinTools: public Toolchain
+{
+public:
+ BuiltinTools(Builder &);
+};
+
+#endif
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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();
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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";
+}
--- /dev/null
+#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
--- /dev/null
+#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();
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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]));
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#ifndef CLANGCOMPILER_H_
+#define CLANGCOMPILER_H_
+
+#include <msp/builder/customizedtool.h>
+
+class ClangCompiler: public CustomizedTool
+{
+public:
+ ClangCompiler(Builder &, const Architecture &, const std::string &);
+};
+
+#endif
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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");
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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));
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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 © = 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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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 © = 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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
+++ /dev/null
-#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();
-}
+++ /dev/null
-#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
+++ /dev/null
-#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 © = 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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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());
-}
+++ /dev/null
-#ifndef ANDROIDARCHIVER_H_
-#define ANDROIDARCHIVER_H_
-
-#include "customizedtool.h"
-
-class AndroidNdk;
-
-class AndroidArchiver: public CustomizedTool
-{
-public:
- AndroidArchiver(Builder &, const Architecture &, const AndroidNdk &);
-};
-
-#endif
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
- }
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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");
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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=='!');
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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 ¤t_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);
- }
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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();
-}
+++ /dev/null
-#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
+++ /dev/null
-#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()));
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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));
-}
+++ /dev/null
-#ifndef BUILTINTOOLS_H_
-#define BUILTINTOOLS_H_
-
-#include "toolchain.h"
-
-class Builder;
-
-class BuiltinTools: public Toolchain
-{
-public:
- BuiltinTools(Builder &);
-};
-
-#endif
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#ifndef CLANGCOMPILER_H_
-#define CLANGCOMPILER_H_
-
-#include "customizedtool.h"
-
-class ClangCompiler: public CustomizedTool
-{
-public:
- ClangCompiler(Builder &, const Architecture &, const std::string &);
-};
-
-#endif
+++ /dev/null
-#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;
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
--- /dev/null
+#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();
+}
--- /dev/null
+#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
--- /dev/null
+#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 ¤t_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);
+ }
+ }
+}
--- /dev/null
+#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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();
-}
+++ /dev/null
-#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
+++ /dev/null
-#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());
-}
+++ /dev/null
-#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
+++ /dev/null
-#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)
-{ }
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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();
-}
+++ /dev/null
-#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
+++ /dev/null
-#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));
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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");
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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));
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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";
-}
+++ /dev/null
-#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
+++ /dev/null
-#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");
-}
+++ /dev/null
-#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
+++ /dev/null
-#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();
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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();
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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 © = 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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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());
-}
+++ /dev/null
-#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
+++ /dev/null
-#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 © = 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));
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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();
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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=='!');
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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();
+}
--- /dev/null
+#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
--- /dev/null
+#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()));
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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());
+}
--- /dev/null
+#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
--- /dev/null
+#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)
+{ }
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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();
+}
--- /dev/null
+#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
--- /dev/null
+#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));
+}
--- /dev/null
+#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
--- /dev/null
+#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";
+}
--- /dev/null
+#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
--- /dev/null
+#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");
+}
--- /dev/null
+#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
--- /dev/null
+#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();
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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();
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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());
+}
--- /dev/null
+#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
--- /dev/null
+#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 © = 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));
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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();
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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));
+}
--- /dev/null
+#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
--- /dev/null
+#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();
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#ifndef TARBALLCOMPONENT_H_
+#define TARBALLCOMPONENT_H_
+
+#include "component.h"
+
+class SourceArchiveComponent: public Component
+{
+public:
+ SourceArchiveComponent(SourcePackage &);
+
+ void create_targets() const override;
+};
+
+#endif
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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
+}
--- /dev/null
+#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
--- /dev/null
+#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();
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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));
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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 © = 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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
- }
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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));
-}
+++ /dev/null
-#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
+++ /dev/null
-#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();
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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";
-}
+++ /dev/null
-#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
+++ /dev/null
-#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();
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#ifndef TARBALLCOMPONENT_H_
-#define TARBALLCOMPONENT_H_
-
-#include "component.h"
-
-class SourceArchiveComponent: public Component
-{
-public:
- SourceArchiveComponent(SourcePackage &);
-
- void create_targets() const override;
-};
-
-#endif
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#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();
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
- }
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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));
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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]));
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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