From: Mikko Rasa Date: Tue, 27 Dec 2022 20:52:34 +0000 (+0200) Subject: Rearrange sources into subdirectories X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=c8e829c219c65ff8e93b6c7b66212ff0876441c5;hp=e2c9c3fffcc61a0c102ccf6a7924e2de709092ad;p=builder.git Rearrange sources into subdirectories --- diff --git a/.gitignore b/.gitignore index f2be0c3..e45ec7e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,13 @@ /.config /builder +/builder.pc /builder-stage1 +/libbuilder.a +/libbuilder.so +/libandroidtools.a +/libbuiltintools.a +/libclangtools.a +/libdatatools.a +/libgnutools.a +/libmsvctools.a /temp diff --git a/Build b/Build index 28ab0e2..74490dd 100644 --- a/Build +++ b/Build @@ -13,9 +13,55 @@ package "builder" standard CXX "c++11"; }; - program "builder" + library "builtintools" + { + source "plugins/builtin"; + default false; + }; + + library "gnutools" + { + source "plugins/gnu"; + default false; + }; + + library "clangtools" + { + source "plugins/clang"; + default false; + }; + + library "msvctools" + { + source "plugins/msvc"; + default false; + }; + + library "androidtools" + { + source "plugins/android"; + default false; + }; + + library "datatools" { - source "source"; + source "plugins/datafile"; + default false; + }; + + library "builder" + { + source "source/lib"; + use "builtintools"; + use "gnutools"; + use "clangtools"; + use "msvctools"; + use "androidtools"; + use "datatools"; + build_info + { + incpath "plugins"; + }; if_arch "windows" { build_info @@ -25,6 +71,17 @@ package "builder" }; }; install true; + install_map + { + map "source/lib" "include/msp/builder"; + }; + }; + + program "builder" + { + source "source/cli"; + use "builder"; + install true; }; source_archive diff --git a/plugins/android/androidapplicationcomponent.cpp b/plugins/android/androidapplicationcomponent.cpp new file mode 100644 index 0000000..be7e31c --- /dev/null +++ b/plugins/android/androidapplicationcomponent.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 contents; + for(const auto &kvp: build_graph.get_targets()) + if(kvp.second->get_package()==&package) + if(InstalledFile *inst = dynamic_cast(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 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 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(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(c) +{ + add("orientation", &AndroidApplicationComponent::orientation); + add("permission", &Loader::permission); +} + +void AndroidApplicationComponent::Loader::permission(const string &perm) +{ + if(!any_equals(obj.permissions, perm)) + obj.permissions.push_back(perm); +} diff --git a/plugins/android/androidapplicationcomponent.h b/plugins/android/androidapplicationcomponent.h new file mode 100644 index 0000000..e1b30e2 --- /dev/null +++ b/plugins/android/androidapplicationcomponent.h @@ -0,0 +1,28 @@ +#ifndef ANDROIDAPPLICATIONCOMPONENT_H_ +#define ANDROIDAPPLICATIONCOMPONENT_H_ + +#include + +class AndroidApplicationComponent: public Component +{ +public: + class Loader: public Msp::DataFile::DerivedObjectLoader + { + public: + Loader(AndroidApplicationComponent &); + + private: + void permission(const std::string &); + }; + +private: + std::string orientation; + std::vector permissions; + +public: + AndroidApplicationComponent(SourcePackage &p, const std::string &n): Component(p, n) { } + + void create_targets() const override; +}; + +#endif diff --git a/plugins/android/androidarchiver.cpp b/plugins/android/androidarchiver.cpp new file mode 100644 index 0000000..617445c --- /dev/null +++ b/plugins/android/androidarchiver.cpp @@ -0,0 +1,14 @@ +#include "androidarchiver.h" +#include "androidtools.h" + +AndroidArchiver::AndroidArchiver(Builder &b, const Architecture &a, const AndroidNdk &ndk): + CustomizedTool(b, "AR", a) +{ + set_command("ar", true); + if(ndk.get_root_dir().empty()) + problems.push_back("Android NDK not found"); + else if(ndk.get_bin_dir().empty()) + problems.push_back("Android NDK toolchain not found"); + else + set_command((ndk.get_bin_dir()/command).str()); +} diff --git a/plugins/android/androidarchiver.h b/plugins/android/androidarchiver.h new file mode 100644 index 0000000..032f832 --- /dev/null +++ b/plugins/android/androidarchiver.h @@ -0,0 +1,14 @@ +#ifndef ANDROIDARCHIVER_H_ +#define ANDROIDARCHIVER_H_ + +#include + +class AndroidNdk; + +class AndroidArchiver: public CustomizedTool +{ +public: + AndroidArchiver(Builder &, const Architecture &, const AndroidNdk &); +}; + +#endif diff --git a/plugins/android/androidassetpackagingtool.cpp b/plugins/android/androidassetpackagingtool.cpp new file mode 100644 index 0000000..ba321cb --- /dev/null +++ b/plugins/android/androidassetpackagingtool.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#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 &sources, const string &) +{ + AndroidManifestFile *manifest = 0; + vector resources; + resources.reserve(sources.size()); + for(Target *s: sources) + { + if(AndroidManifestFile *m = dynamic_cast(s)) + manifest = m; + else if(FileTarget *f = dynamic_cast(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(*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 resource_dirs; + resource_dirs.reserve(res.get_dependencies().size()); + for(Target *d: res.get_dependencies()) + { + FileTarget *file = dynamic_cast(d); + Target *real = d->get_real_target(); + + if(dynamic_cast(real)) + { + argv.push_back("-M"); + argv.push_back(FS::relative(file->get_path(), work_dir).str()); + } + else if(real->get_package()==res.get_package()) + { + const FS::Path &path = file->get_path(); + FS::Path res_dir = path.subpath(0, path.size()-2); + if(!any_equals(resource_dirs, res_dir)) + resource_dirs.push_back(res_dir); + } + } + + for(const FS::Path &d: resource_dirs) + { + argv.push_back("-S"); + argv.push_back(FS::relative(d, work_dir).str()); + } + + return argv; +} diff --git a/plugins/android/androidassetpackagingtool.h b/plugins/android/androidassetpackagingtool.h new file mode 100644 index 0000000..c03c097 --- /dev/null +++ b/plugins/android/androidassetpackagingtool.h @@ -0,0 +1,23 @@ +#ifndef ANDROIDASSETPACKAGINGTOOL_H_ +#define ANDROIDASSETPACKAGINGTOOL_H_ + +#include + +class AndroidResourceBundle; +class AndroidSdk; + +class AndroidAssetPackagingTool: public Tool +{ +private: + const AndroidSdk &sdk; + +public: + AndroidAssetPackagingTool(Builder &, const AndroidSdk &); + + Target *create_target(const std::vector &, const std::string &) override; + +private: + static ExternalTask::Arguments _run(const AndroidResourceBundle &, Msp::FS::Path &); +}; + +#endif diff --git a/plugins/android/androidcompiler.cpp b/plugins/android/androidcompiler.cpp new file mode 100644 index 0000000..b9d7add --- /dev/null +++ b/plugins/android/androidcompiler.cpp @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#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(tool).get_architecture(); + + CustomizedTool::do_prepare(tool); + if(tag=="CXX") + { + tool.build_info.libs.push_back("gnustl_static"); + + unsigned version = tool.extra_data; + string version_str = format("%d.%d.%d", version>>16, (version>>8)&0xFF, version&0xFF); + FS::Path libstdcxx_dir = ndk.get_root_dir()/"sources"/"cxx-stl"/"gnu-libstdc++"; + FS::Path libstdcxx_path; + while(1) + { + libstdcxx_path = libstdcxx_dir/version_str; + if(FS::exists(libstdcxx_path)) + break; + + string::size_type dot = version_str.rfind('.'); + if(dot==string::npos) + { + tool.problems.push_back("C++ standard library not found"); + return; + } + + version_str = version_str.substr(0, dot); + } + + builder.get_logger().log("tools", "Found GNU libstdc++ in %s", libstdcxx_path); + + FS::Path public_dir = libstdcxx_path/"include"; + tool.system_path.push_back(public_dir); + tool.build_info.incpath.push_back(public_dir); + + FS::Path arch_path = libstdcxx_path/"libs"; + builder.get_logger().log("files", "Traversing %s", arch_path.str()); + string arch_dir = arch.best_match(list_files(arch_path)); + if(!arch_dir.empty()) + { + tool.build_info.incpath.push_back(libstdcxx_path/"libs"/arch_dir/"include"); + tool.build_info.libpath.push_back(libstdcxx_path/"libs"/arch_dir); + } + } +} diff --git a/plugins/android/androidcompiler.h b/plugins/android/androidcompiler.h new file mode 100644 index 0000000..7bfbe11 --- /dev/null +++ b/plugins/android/androidcompiler.h @@ -0,0 +1,20 @@ +#ifndef ANDROIDCOMPILER_H_ +#define ANDROIDCOMPILER_H_ + +#include + +class AndroidNdk; + +class AndroidCompiler: public CustomizedTool +{ +private: + const AndroidNdk &ndk; + +public: + AndroidCompiler(Builder &, const Architecture &, const std::string &, const AndroidNdk &); + +protected: + void do_prepare(ToolData &) const override; +}; + +#endif diff --git a/plugins/android/androidlinker.cpp b/plugins/android/androidlinker.cpp new file mode 100644 index 0000000..ec90249 --- /dev/null +++ b/plugins/android/androidlinker.cpp @@ -0,0 +1,18 @@ +#include "androidlinker.h" +#include "androidtools.h" + +using namespace std; + +AndroidLinker::AndroidLinker(Builder &b, const Architecture &a, const AndroidNdk &ndk): + CustomizedTool(b, "LINK", a) +{ + build_info.sysroot = ndk.get_platform_sysroot(); + + set_command("gcc", true); + set_command((ndk.get_bin_dir()/command).str()); +} + +Target *AndroidLinker::create_target(const vector &sources, const string &) +{ + return CustomizedTool::create_target(sources, "shared"); +} diff --git a/plugins/android/androidlinker.h b/plugins/android/androidlinker.h new file mode 100644 index 0000000..71d6bc4 --- /dev/null +++ b/plugins/android/androidlinker.h @@ -0,0 +1,16 @@ +#ifndef ANDROIDLINKER_H_ +#define ANDROIDLINKER_H_ + +#include + +class AndroidNdk; + +class AndroidLinker: public CustomizedTool +{ +public: + AndroidLinker(Builder &, const Architecture &, const AndroidNdk &); + + Target *create_target(const std::vector &, const std::string &) override; +}; + +#endif diff --git a/plugins/android/androidmanifestfile.cpp b/plugins/android/androidmanifestfile.cpp new file mode 100644 index 0000000..ce41769 --- /dev/null +++ b/plugins/android/androidmanifestfile.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include "androidapplicationcomponent.h" +#include "androidmanifestfile.h" + +using namespace std; +using namespace Msp; + +AndroidManifestFile::AndroidManifestFile(Builder &b, const AndroidApplicationComponent &a): + FileTarget(b, a.get_package(), a.get_package().get_temp_directory()/a.get_name()/"AndroidManifest.xml") +{ + component = &a; + tool = &builder.get_toolchain().get_tool("AMG"); + + add_dependency(package->get_build_file()); +} + +void AndroidManifestFile::set_native_library(SharedLibrary *lib) +{ + native_lib = lib; +} + +void AndroidManifestFile::set_orientation(const string &ori) +{ + orientation = ori; +} + +void AndroidManifestFile::add_permission(const string &perm) +{ + if(!any_equals(permissions, perm)) + permissions.push_back(perm); +} diff --git a/plugins/android/androidmanifestfile.h b/plugins/android/androidmanifestfile.h new file mode 100644 index 0000000..ad6a2da --- /dev/null +++ b/plugins/android/androidmanifestfile.h @@ -0,0 +1,34 @@ +#ifndef ANDROIDMANIFESTFILE_H_ +#define ANDROIDMANIFESTFILE_H_ + +#include +#include + +class AndroidApplicationComponent; +class SharedLibrary; + +/** +Metadata file for an Android application. +*/ +class AndroidManifestFile: public FileTarget +{ +private: + SharedLibrary *native_lib = 0; + std::vector 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 &get_permissions() const { return permissions; } + const std::string &get_orientation() const { return orientation; } +}; + +#endif diff --git a/plugins/android/androidmanifestgenerator.cpp b/plugins/android/androidmanifestgenerator.cpp new file mode 100644 index 0000000..cf03bee --- /dev/null +++ b/plugins/android/androidmanifestgenerator.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#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 &, 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("\n"); + IO::print(out, "\n", comp.get_name()); + out.write("\t\n"); + // TODO Make the icon name configurable + bool debuggable = binfo.debug; + IO::print(out, "\t\n", pkg.get_label(), debuggable); + if(SharedLibrary *native_lib = manifest.get_native_library()) + { + out.write("\t\t\n"); + IO::print(out, "\t\t\t\n", native_lib->get_libname()); + out.write("\t\t\t\n"); + out.write("\t\t\t\t\n"); + out.write("\t\t\t\t\n"); + out.write("\t\t\t\n"); + out.write("\t\t\n"); + } + out.write("\t\n"); + for(const string &p: manifest.get_permissions()) + IO::print(out, "\t\n", p); + out.write("\n"); + + return true; +} diff --git a/plugins/android/androidmanifestgenerator.h b/plugins/android/androidmanifestgenerator.h new file mode 100644 index 0000000..d1d1531 --- /dev/null +++ b/plugins/android/androidmanifestgenerator.h @@ -0,0 +1,19 @@ +#ifndef ANDROIDMANIFESTGENERATOR_H_ +#define ANDROIDMANIFESTGENERATOR_H_ + +#include + +class AndroidManifestFile; + +class AndroidManifestGenerator: public Tool +{ +public: + AndroidManifestGenerator(Builder &); + + Target *create_target(const std::vector &, const std::string &) override; + +private: + static bool _run(const AndroidManifestFile &); +}; + +#endif diff --git a/plugins/android/androidpackagefile.cpp b/plugins/android/androidpackagefile.cpp new file mode 100644 index 0000000..00b5dad --- /dev/null +++ b/plugins/android/androidpackagefile.cpp @@ -0,0 +1,16 @@ +#include +#include +#include "androidpackagefile.h" +#include "androidresourcebundle.h" + +using namespace std; + +AndroidPackageFile::AndroidPackageFile(Builder &b, const Component &c, AndroidResourceBundle &resource_bundle, const vector &other_files): + FileTarget(b, c.get_package(), c.get_package().get_output_directory()/(c.get_name()+".apk")) +{ + component = &c; + + add_dependency(resource_bundle); + for(FileTarget *f: other_files) + add_dependency(*f); +} diff --git a/plugins/android/androidpackagefile.h b/plugins/android/androidpackagefile.h new file mode 100644 index 0000000..91b6b36 --- /dev/null +++ b/plugins/android/androidpackagefile.h @@ -0,0 +1,16 @@ +#ifndef ANDROIDPACKAGEFILE_H_ +#define ANDROIDPACKAGEFILE_H_ + +#include + +class AndroidResourceBundle; + +class AndroidPackageFile: public FileTarget +{ +public: + AndroidPackageFile(Builder &, const Component &, AndroidResourceBundle &, const std::vector &); + + const char *get_type() const override { return "AndroidPackageFile"; } +}; + +#endif diff --git a/plugins/android/androidresourcebundle.cpp b/plugins/android/androidresourcebundle.cpp new file mode 100644 index 0000000..5f2c43c --- /dev/null +++ b/plugins/android/androidresourcebundle.cpp @@ -0,0 +1,16 @@ +#include +#include +#include "androidmanifestfile.h" +#include "androidresourcebundle.h" + +using namespace std; + +AndroidResourceBundle::AndroidResourceBundle(Builder &b, const Component &c, AndroidManifestFile &manifest, const vector &resources): + FileTarget(b, c.get_package(), c.get_package().get_temp_directory()/c.get_name()/(c.get_name()+".ap_")) +{ + component = &c; + + add_dependency(manifest); + for(FileTarget *f: resources) + add_dependency(*f); +} diff --git a/plugins/android/androidresourcebundle.h b/plugins/android/androidresourcebundle.h new file mode 100644 index 0000000..bf11509 --- /dev/null +++ b/plugins/android/androidresourcebundle.h @@ -0,0 +1,16 @@ +#ifndef ANDROIDRESOURCEBUNDLE_H_ +#define ANDROIDRESOURCEBUNDLE_H_ + +#include + +class AndroidManifestFile; + +class AndroidResourceBundle: public FileTarget +{ +public: + AndroidResourceBundle(Builder &, const Component &, AndroidManifestFile &, const std::vector &); + + const char *get_type() const override { return "AndroidResourceBundle"; } +}; + +#endif diff --git a/plugins/android/androidresourcefile.cpp b/plugins/android/androidresourcefile.cpp new file mode 100644 index 0000000..430c0a8 --- /dev/null +++ b/plugins/android/androidresourcefile.cpp @@ -0,0 +1,19 @@ +#include +#include +#include "androidresourcefile.h" + +using namespace std; +using namespace Msp; + +AndroidResourceFile::AndroidResourceFile(Builder &b, const Component &c, const FS::Path &p): + FileTarget(b, c.get_package(), p) +{ + string ext = FS::extpart(FS::basename(p)); + if(ext==".png" || ext==".jpg") + resource_type = "drawable"; + // TODO recognize various xml files; must inspect contents + else + resource_type = "raw"; + + install_location = "res/"+resource_type; +} diff --git a/plugins/android/androidresourcefile.h b/plugins/android/androidresourcefile.h new file mode 100644 index 0000000..7b03287 --- /dev/null +++ b/plugins/android/androidresourcefile.h @@ -0,0 +1,20 @@ +#ifndef ANDROIDRESOURCEFILE_H_ +#define ANDROIDRESOURCEFILE_H_ + +#include + +/** +A file destined to be used as a resource in an Android application. +*/ +class AndroidResourceFile: public FileTarget +{ +private: + std::string resource_type; + +public: + AndroidResourceFile(Builder &, const Component &, const Msp::FS::Path &); + + const char *get_type() const override { return "AndroidResourceFile"; } +}; + +#endif diff --git a/plugins/android/androidtools.cpp b/plugins/android/androidtools.cpp new file mode 100644 index 0000000..5053682 --- /dev/null +++ b/plugins/android/androidtools.cpp @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 version_parts = split(version_str, '.'); + unsigned version = lexical_cast(version_parts[0])<<16; + if(version_parts.size()>1) + version += lexical_cast(version_parts[1])<<8; + if(version_parts.size()>2) + version += lexical_cast(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(p.substr(8)); + if(api>63) + builder.get_logger().log("problems", "API level %d is too high", api); + else + supported_api_levels |= static_cast(1)<>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; (jlatest_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 platform_archs = list_filtered(platform_archs_dir, "^arch-"); + for(string &a: platform_archs) + a.erase(0, 5); + string use_arch = architecture.best_match(platform_archs); + + if(use_arch.empty()) + { + builder.get_logger().log("problems", "No matching Android NDK platform found"); + return; + } + + platform_sysroot = platform_archs_dir/("arch-"+use_arch); + builder.get_logger().log("tools", "Android NDK platform sysroot is %s", platform_sysroot); +} + + +AndroidTools::AndroidTools(Builder &builder, const Architecture &arch): + Toolchain("android-gnu", get_priority(arch)), + sdk(builder), + ndk(builder, arch, sdk) +{ + if(uint64_t common_api_levels = sdk.get_supported_api_levels()&ndk.get_supported_api_levels()) + { + unsigned api = 0; + for(unsigned i=32; i>0; i>>=1) + api += i*!!(common_api_levels>>(api+i)); + builder.get_logger().log("tools", "Using Android API level %d", api); + sdk.select_api_level(api); + ndk.select_api_level(api); + } + else + builder.get_logger().log("problems", "No usable Android platforms found"); + + add_tool(new AndroidCompiler(builder, arch, "CC", ndk)); + add_tool(new AndroidCompiler(builder, arch, "CXX", ndk)); + add_tool(new AndroidLinker(builder, arch, ndk)); + add_tool(new AndroidArchiver(builder, arch, ndk)); + add_tool(new AndroidManifestGenerator(builder)); + add_tool(new AndroidAssetPackagingTool(builder, sdk)); + add_tool(new ApkBuilder(builder)); + add_tool(new JarSigner(builder)); +} + +int AndroidTools::get_priority(const Architecture &arch) +{ + if(arch.get_system()=="android") + return 30; + else + return -10; +} diff --git a/plugins/android/androidtools.h b/plugins/android/androidtools.h new file mode 100644 index 0000000..a5fd560 --- /dev/null +++ b/plugins/android/androidtools.h @@ -0,0 +1,85 @@ +#ifndef ANDROIDTOOLS_H_ +#define ANDROIDTOOLS_H_ + +#include +#include +#include + +class Architecture; +class Builder; + +class AndroidDevKit +{ +protected: + Builder &builder; + Msp::FS::Path root; + /* Needs refactoring if API levels go over 63. At present rate this will + take decades to occur. */ + uint64_t supported_api_levels = 0; + + AndroidDevKit(Builder &, const std::string &, const Msp::FS::Path & = Msp::FS::Path()); + ~AndroidDevKit() { } + +public: + const Msp::FS::Path &get_root_dir() const { return root; } + uint64_t get_supported_api_levels() const { return supported_api_levels; } + void select_api_level(unsigned); +protected: + virtual void init_api_level(unsigned) = 0; +}; + +class AndroidSdk: public AndroidDevKit +{ +private: + Msp::FS::Path build_tools_dir; + // TODO use a FileTarget for the jar + Msp::FS::Path platform_jar; + +public: + AndroidSdk(Builder &); + +private: + void find_build_tools_dir(); + void init_api_level(unsigned) override; + +public: + const Msp::FS::Path &get_build_tools_dir() const { return build_tools_dir; } + const Msp::FS::Path &get_platform_jar() const { return platform_jar; } +}; + +class AndroidNdk: public AndroidDevKit +{ +private: + const Architecture &architecture; + Msp::FS::Path bin_dir; + Msp::FS::Path common_sysroot; + Msp::FS::Path platform_sysroot; + +public: + AndroidNdk(Builder &, const Architecture &, const AndroidSdk &); +private: + static Msp::FS::Path create_default_path(const AndroidSdk &); + + void find_toolchain_dir(); + void init_api_level(unsigned) override; + +public: + const Msp::FS::Path &get_bin_dir() const { return bin_dir; } + const Msp::FS::Path &get_common_sysroot() const { return common_sysroot; } + const Msp::FS::Path &get_platform_sysroot() const { return platform_sysroot; } +}; + + +class AndroidTools: public Toolchain +{ +private: + AndroidSdk sdk; + AndroidNdk ndk; + +public: + AndroidTools(Builder &, const Architecture &); + + static int get_priority(const Architecture &); +}; + +#endif diff --git a/plugins/android/apkbuilder.cpp b/plugins/android/apkbuilder.cpp new file mode 100644 index 0000000..ec20854 --- /dev/null +++ b/plugins/android/apkbuilder.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include +#include +#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 &sources, const string &) +{ + AndroidResourceBundle *resource_bundle = 0; + vector other_files; + other_files.reserve(sources.size()); + for(Target *s: sources) + { + if(AndroidResourceBundle *r = dynamic_cast(s)) + resource_bundle = r; + else if(FileTarget *f = dynamic_cast(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(tgt); + const ApkBuilder &tool = dynamic_cast(*apk.get_tool()); + + ExternalTask::Arguments argv; + argv.push_back(tool.get_executable()->get_path().str()); + argv.push_back("u"); + + FS::Path input_path; + vector files; + files.reserve(apk.get_dependencies().size()); + for(Target *d: apk.get_dependencies()) + { + FileTarget *file = dynamic_cast(d); + Target *real = d->get_real_target(); + + if(dynamic_cast(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()->run(apk)); + return chain; +} diff --git a/plugins/android/apkbuilder.h b/plugins/android/apkbuilder.h new file mode 100644 index 0000000..b4e534f --- /dev/null +++ b/plugins/android/apkbuilder.h @@ -0,0 +1,21 @@ +#ifndef APKBUILDER_H_ +#define APKBUILDER_H_ + +#include + +class AndroidPackageFile; + +class ApkBuilder: public Tool +{ +public: + ApkBuilder(Builder &); + + Target *create_target(const std::vector &, const std::string &) override; +protected: + void do_prepare(ToolData &) const override; + +private: + static Task *_run(const Target &); +}; + +#endif diff --git a/plugins/android/jarsigner.cpp b/plugins/android/jarsigner.cpp new file mode 100644 index 0000000..9738351 --- /dev/null +++ b/plugins/android/jarsigner.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +#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 &, const string &) +{ + throw logic_error("not implemented"); +} + +ExternalTask::Arguments JarSigner::_run(const FileTarget &file, FS::Path &work_dir) +{ + const Tool &tool = *file.get_tool(); + + ExternalTask::Arguments argv; + argv.push_back(tool.get_executable()->get_path().str()); + + // TODO Make this generic + FS::Path home_dir = Msp::getenv("HOME"); + argv.push_back("-keystore"); + argv.push_back((home_dir/".android"/"debug.keystore").str()); + argv.push_back("-storepass"); + argv.push_back("android"); + + argv.push_back(FS::relative(file.get_path(), work_dir).str()); + argv.push_back("androiddebugkey"); + + return argv; +} diff --git a/plugins/android/jarsigner.h b/plugins/android/jarsigner.h new file mode 100644 index 0000000..6c97373 --- /dev/null +++ b/plugins/android/jarsigner.h @@ -0,0 +1,19 @@ +#ifndef JARSIGNER_H_ +#define JARSIGNER_H_ + +#include + +class FileTarget; + +class JarSigner: public Tool +{ +public: + JarSigner(Builder &); + + Target *create_target(const std::vector &, const std::string &) override; + +private: + static ExternalTask::Arguments _run(const FileTarget &, Msp::FS::Path &); +}; + +#endif diff --git a/plugins/builtin/builtintools.cpp b/plugins/builtin/builtintools.cpp new file mode 100644 index 0000000..c2c11e6 --- /dev/null +++ b/plugins/builtin/builtintools.cpp @@ -0,0 +1,17 @@ +#include "builtintools.h" +#include "copy.h" +#include "compilecommandsgenerator.h" +#include "pkgconfiggenerator.h" +#include "tar.h" +#include "vcxprojectgenerator.h" +#include "vssolutiongenerator.h" + +BuiltinTools::BuiltinTools(Builder &builder) +{ + add_tool(new Copy(builder)); + add_tool(new Tar(builder)); + add_tool(new PkgConfigGenerator(builder)); + add_tool(new VcxProjectGenerator(builder)); + add_tool(new VsSolutionGenerator(builder)); + add_tool(new CompileCommandsGenerator(builder)); +} diff --git a/plugins/builtin/builtintools.h b/plugins/builtin/builtintools.h new file mode 100644 index 0000000..4771e83 --- /dev/null +++ b/plugins/builtin/builtintools.h @@ -0,0 +1,14 @@ +#ifndef BUILTINTOOLS_H_ +#define BUILTINTOOLS_H_ + +#include + +class Builder; + +class BuiltinTools: public Toolchain +{ +public: + BuiltinTools(Builder &); +}; + +#endif diff --git a/plugins/builtin/compilecommandsgenerator.cpp b/plugins/builtin/compilecommandsgenerator.cpp new file mode 100644 index 0000000..61e11db --- /dev/null +++ b/plugins/builtin/compilecommandsgenerator.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +#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 &, 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(kvp.second)) + { + FS::Path src_path = obj->get_source().get_path(); + Task *task = kvp.second->build(); + if(!first) + out.put(','); + IO::print(out, "\n\t{\n"); + IO::print(out, "\t\t\"file\": \"%s\"\n", src_path); + IO::print(out, "\t\t\"command\": \"%s\"\n", c_escape(task->get_command())); + IO::print(out, "\t\t\"directory\": \"%s\"\n", work_dir); + IO::print(out, "\t}"); + delete task; + first = false; + } + + IO::print(out, "\n]\n"); + + return true; +} diff --git a/plugins/builtin/compilecommandsgenerator.h b/plugins/builtin/compilecommandsgenerator.h new file mode 100644 index 0000000..ec4a501 --- /dev/null +++ b/plugins/builtin/compilecommandsgenerator.h @@ -0,0 +1,19 @@ +#ifndef COMPILECOMMANDSGENERATOR_H_ +#define COMPILECOMMANDSGENERATOR_H_ + +#include + +class CompileCommandsJson; + +class CompileCommandsGenerator: public Tool +{ +public: + CompileCommandsGenerator(Builder &); + + Target *create_target(const std::vector &, const std::string &) override; + +private: + static bool _run(const CompileCommandsJson &); +}; + +#endif diff --git a/plugins/builtin/compilecommandsjson.cpp b/plugins/builtin/compilecommandsjson.cpp new file mode 100644 index 0000000..d4adc14 --- /dev/null +++ b/plugins/builtin/compilecommandsjson.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#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(kvp.second)) + kvp.second->prepare(); +} diff --git a/plugins/builtin/compilecommandsjson.h b/plugins/builtin/compilecommandsjson.h new file mode 100644 index 0000000..be9c448 --- /dev/null +++ b/plugins/builtin/compilecommandsjson.h @@ -0,0 +1,20 @@ +#ifndef COMPILECOMMANDSJSON_H_ +#define COMPILECOMMANDSJSON_H_ + +#include +#include + +class CompileCommandsJson: public FileTarget +{ +private: + +public: + CompileCommandsJson(Builder &, const SourcePackage &); + + const char *get_type() const override { return "CompileCommandsJson"; } + +protected: + void find_dependencies() override; +}; + +#endif diff --git a/plugins/builtin/copy.cpp b/plugins/builtin/copy.cpp new file mode 100644 index 0000000..0633ff9 --- /dev/null +++ b/plugins/builtin/copy.cpp @@ -0,0 +1,73 @@ +#ifndef _WIN32 +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#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 &sources, const string &arg) +{ + FileTarget &file_tgt = dynamic_cast(*sources.front()); + InstalledFile *inst = new InstalledFile(builder, *file_tgt.get_package(), file_tgt, arg); + inst->set_tool(*this); + return inst; +} + +bool Copy::_run(const InstalledFile &install) +{ + const FileTarget &source = install.get_source(); + const FS::Path &src_path = source.get_path(); + const FS::Path &dst_path = install.get_path(); + + try + { + IO::File in(src_path.str()); + IO::File out(dst_path.str(), IO::M_WRITE); + + // Actual transfer loop + char buf[16384]; + while(!in.eof()) + { + unsigned len = in.read(buf, sizeof(buf)); + out.write(buf, len); + } + } + catch(const exception &e) + { + IO::print(IO::cerr, "%s\n", e.what()); + return false; + } + +#ifndef _WIN32 + // Preserve file permissions + struct stat st; + if(stat(src_path.str().c_str(), &st)==0) + chmod(dst_path.str().c_str(), st.st_mode&0777); + + const FS::Path &link = install.get_symlink(); + if(!link.empty()) + { + FS::Path relpath = FS::relative(dst_path, FS::dirname(link)); + if(FS::exists(link)) + FS::unlink(link); + symlink(relpath.str().c_str(), link.str().c_str()); + } +#endif + + return true; +} diff --git a/plugins/builtin/copy.h b/plugins/builtin/copy.h new file mode 100644 index 0000000..f6f01fb --- /dev/null +++ b/plugins/builtin/copy.h @@ -0,0 +1,22 @@ +#ifndef COPY_H_ +#define COPY_H_ + +#include + +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 &, const std::string &) override; + +private: + static bool _run(const InstalledFile &); +}; + +#endif diff --git a/plugins/builtin/pkgconfigfile.cpp b/plugins/builtin/pkgconfigfile.cpp new file mode 100644 index 0000000..70ab59d --- /dev/null +++ b/plugins/builtin/pkgconfigfile.cpp @@ -0,0 +1,11 @@ +#include +#include +#include "pkgconfigfile.h" + +PkgConfigFile::PkgConfigFile(Builder &b, const SourcePackage &p): + FileTarget(b, p, p.get_source_directory()/(p.get_name()+".pc")) +{ + tool = &builder.get_toolchain().get_tool("PCG"); + + install_location = "lib/pkgconfig"; +} diff --git a/plugins/builtin/pkgconfigfile.h b/plugins/builtin/pkgconfigfile.h new file mode 100644 index 0000000..23027e4 --- /dev/null +++ b/plugins/builtin/pkgconfigfile.h @@ -0,0 +1,18 @@ +#ifndef PKGCONFIGFILE_H_ +#define PKGCONFIGFILE_H_ + +#include +#include + +/** +Creates a .pc file to enable other packages fetch build options with pkg-config. +*/ +class PkgConfigFile: public FileTarget +{ +public: + PkgConfigFile(Builder &, const SourcePackage &); + + const char *get_type() const override { return "PkgConfigFile"; } +}; + +#endif diff --git a/plugins/builtin/pkgconfiggenerator.cpp b/plugins/builtin/pkgconfiggenerator.cpp new file mode 100644 index 0000000..76ed7d9 --- /dev/null +++ b/plugins/builtin/pkgconfiggenerator.cpp @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#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 &, const string &) +{ + throw logic_error("Not implemented"); +} + +bool PkgConfigGenerator::_run(const PkgConfigFile &pkgc) +{ + Builder &builder = pkgc.get_package()->get_builder(); + const SourcePackage &spkg = *pkgc.get_package(); + + IO::BufferedFile out(pkgc.get_path().str(), IO::M_WRITE); + IO::print(out, "prefix=%s\n", builder.get_prefix().str()); + IO::print(out, "source=%s\n\n", spkg.get_source_directory()); + + IO::print(out, "Name: %s\n", spkg.get_label()); + IO::print(out, "Description: %s\n", spkg.get_description()); + IO::print(out, "Version: %s\n", spkg.get_version()); + + IO::print(out, "Requires:"); + for(const Package *r: spkg.get_required_packages()) + if(r->uses_pkgconfig()) + IO::print(out, " %s", r->get_name()); + out.put('\n'); + + const BuildInfo &binfo = spkg.get_exported_build_info(); + IO::print(out, "Libs:"); + for(const FS::Path &p: binfo.libpath) + IO::print(out, " -L%s", prefixify(p, builder.get_prefix())); + for(const string &l: binfo.libs) + IO::print(out, " -l%s", l); + if(binfo.threads) + out.write("-pthread"); + out.put('\n'); + + IO::print(out, "Cflags:"); + for(const FS::Path &p: binfo.incpath) + IO::print(out, " -I%s", prefixify(p, builder.get_prefix())); + for(const auto &kvp: binfo.defines) + if(kvp.second.empty()) + IO::print(out, " -D%s", kvp.first); + else + IO::print(out, " -D%s=%s", kvp.first, kvp.second); + out.put('\n'); + + return true; +} + +string PkgConfigGenerator::prefixify(const FS::Path &path, const FS::Path &prefix) +{ + if(FS::descendant_depth(path, prefix)>=0) + { + FS::Path rel_path = FS::relative(path, prefix); + return "${prefix}"+rel_path.str().substr(1); + } + else + return path.str(); +} diff --git a/plugins/builtin/pkgconfiggenerator.h b/plugins/builtin/pkgconfiggenerator.h new file mode 100644 index 0000000..2a0e0b1 --- /dev/null +++ b/plugins/builtin/pkgconfiggenerator.h @@ -0,0 +1,20 @@ +#ifndef PKGCONFIGGENERATOR_H_ +#define PKGCONFIGGENERATOR_H_ + +#include + +class PkgConfigFile; + +class PkgConfigGenerator: public Tool +{ +public: + PkgConfigGenerator(Builder &); + + Target *create_target(const std::vector &, const std::string &) override; + +private: + static bool _run(const PkgConfigFile &); + static std::string prefixify(const Msp::FS::Path &, const Msp::FS::Path &); +}; + +#endif diff --git a/plugins/builtin/tar.cpp b/plugins/builtin/tar.cpp new file mode 100644 index 0000000..4bac32a --- /dev/null +++ b/plugins/builtin/tar.cpp @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include +#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 &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(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(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 + +class TarBall; + +class Tar: public Tool +{ +public: + Tar(Builder &); + + Target *create_target(const std::vector &, const std::string &) override; + +private: + static bool _run(const TarBall &); + static void store_number(char *, unsigned, unsigned); +}; + +#endif diff --git a/plugins/builtin/tarball.cpp b/plugins/builtin/tarball.cpp new file mode 100644 index 0000000..0b2d481 --- /dev/null +++ b/plugins/builtin/tarball.cpp @@ -0,0 +1,14 @@ +#include +#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(package); +} diff --git a/plugins/builtin/tarball.h b/plugins/builtin/tarball.h new file mode 100644 index 0000000..f63840e --- /dev/null +++ b/plugins/builtin/tarball.h @@ -0,0 +1,15 @@ +#ifndef TARBALL_H_ +#define TARBALL_H_ + +#include + +class TarBall: public FileTarget +{ +public: + TarBall(Builder &, const SourcePackage &, const std::string &); + + const char *get_type() const override { return "TarBall"; } + const SourcePackage *get_package() const; +}; + +#endif diff --git a/plugins/builtin/vcxprojectfile.cpp b/plugins/builtin/vcxprojectfile.cpp new file mode 100644 index 0000000..3ec7da1 --- /dev/null +++ b/plugins/builtin/vcxprojectfile.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#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(digest[j])); + } +} diff --git a/plugins/builtin/vcxprojectfile.h b/plugins/builtin/vcxprojectfile.h new file mode 100644 index 0000000..1f85d0e --- /dev/null +++ b/plugins/builtin/vcxprojectfile.h @@ -0,0 +1,19 @@ +#ifndef VCXPROJECTFILE_H_ +#define VCXPROJECTFILE_H_ + +#include + +class VcxProjectFile: public FileTarget +{ +private: + std::string guid; + +public: + VcxProjectFile(Builder &, const SourcePackage &); + + const char *get_type() const override { return "VcxProjectFile"; } + + const std::string &get_guid() const { return guid; } +}; + +#endif diff --git a/plugins/builtin/vcxprojectgenerator.cpp b/plugins/builtin/vcxprojectgenerator.cpp new file mode 100644 index 0000000..7cb9faf --- /dev/null +++ b/plugins/builtin/vcxprojectgenerator.cpp @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#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 &, 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, "\n"); + + IO::print(out, "\t\n"); + vector 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\n", b, p); + IO::print(out, "\t\t\t%s\n", b); + IO::print(out, "\t\t\t%s\n", p); + IO::print(out, "\t\t\n"); + } + IO::print(out, "\t\n"); + + IO::print(out, "\t\n"); + IO::print(out, "\t\t15.0\n"); + IO::print(out, "\t\tMakeFileProj\n"); + IO::print(out, "\t\t{%s}\n", project.get_guid()); + IO::print(out, "\t\n"); + + IO::print(out, "\t\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(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\n", b, p); + IO::print(out, "\t\tMakeFile\n"); + IO::print(out, "\t\t%s\n", base_cmd); + IO::print(out, "\t\t%s -c\n", base_cmd); + IO::print(out, "\t\t%s -B\n", base_cmd); + if(exe) + IO::print(out, "\t\t%s\n", exe->get_path()); + IO::print(out, "\t\n"); + } + + IO::print(out, "\t\n"); + + vector sources; + vector includes; + vector 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(kvp.second)) + { + if(dynamic_cast(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\n"); + string path_str; + for(const FS::Path &p: build_info.incpath) + append(path_str, ";", p.str()); + IO::print(out, "\t\t%s\n", path_str); + IO::print(out, "\t\n"); + } + + IO::print(out, "\t\n"); + for(const FileTarget *s: sources) + IO::print(out, "\t\t\n", s->get_path()); + IO::print(out, "\t\n"); + + IO::print(out, "\t\n"); + for(const FileTarget *i: includes) + IO::print(out, "\t\t\n", i->get_path()); + IO::print(out, "\t\n"); + + IO::print(out, "\t\n"); + for(const FileTarget *t: others) + IO::print(out, "\t\t\n", t->get_path()); + IO::print(out, "\t\n"); + + IO::print(out, "\t\n"); + IO::print(out, "\n"); + + return true; +} diff --git a/plugins/builtin/vcxprojectgenerator.h b/plugins/builtin/vcxprojectgenerator.h new file mode 100644 index 0000000..ce96a54 --- /dev/null +++ b/plugins/builtin/vcxprojectgenerator.h @@ -0,0 +1,19 @@ +#ifndef VCXPROJECTGENERATOR_H_ +#define VCXPROJECTGENERATOR_H_ + +#include + +class VcxProjectFile; + +class VcxProjectGenerator: public Tool +{ +public: + VcxProjectGenerator(Builder &); + + Target *create_target(const std::vector &, const std::string &) override; + +private: + static bool _run(const VcxProjectFile &); +}; + +#endif diff --git a/plugins/builtin/vssolutionfile.cpp b/plugins/builtin/vssolutionfile.cpp new file mode 100644 index 0000000..691e1c6 --- /dev/null +++ b/plugins/builtin/vssolutionfile.cpp @@ -0,0 +1,31 @@ +#include +#include +#include +#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(*i)) + { + if(FileTarget *project = builder.get_vfs().get_target(spkg->get_source_directory()/(spkg->get_name()+".vcxproj"))) + add_dependency(*project); + + for(Package *r: spkg->get_required_packages()) + if(!any_equals(reqs, r)) + reqs.push_back(r); + } +} diff --git a/plugins/builtin/vssolutionfile.h b/plugins/builtin/vssolutionfile.h new file mode 100644 index 0000000..402caa7 --- /dev/null +++ b/plugins/builtin/vssolutionfile.h @@ -0,0 +1,16 @@ +#ifndef VSSOLUTIONFILE_H_ +#define VSSOLUTIONFILE_H_ + +#include + +class VsSolutionFile: public FileTarget +{ +public: + VsSolutionFile(Builder &, const SourcePackage &); + + const char *get_type() const override { return "VsSolutionFile"; } +protected: + void find_dependencies() override; +}; + +#endif diff --git a/plugins/builtin/vssolutiongenerator.cpp b/plugins/builtin/vssolutiongenerator.cpp new file mode 100644 index 0000000..60602e1 --- /dev/null +++ b/plugins/builtin/vssolutiongenerator.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#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 &, 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 projects; + for(const Target *t: solution.get_dependencies()) + if(const VcxProjectFile *project = dynamic_cast(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 build_types = builder.get_build_types(); + const char *platforms[] = { "x86", "x64" }; + + IO::print(out, "Global\n"); + IO::print(out, "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n"); + for(const string &t: build_types) + for(const char *p: platforms) + IO::print(out, "\t\t%s|%s = %s|%s\n", t, p, t, p); + IO::print(out, "\tEndGlobalSection\n"); + IO::print(out, "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n"); + for(const VcxProjectFile *p: projects) + for(const string &t: build_types) + for(const char *f: platforms) + { + const char *project_platform = (!strcmp(f, "x86") ? "Win32" : f); + IO::print(out, "\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n", p->get_guid(), t, f, t, project_platform); + if(p->get_package()==&spkg) + IO::print(out, "\t\t{%s}.%s|%s.Build.0 = %s|%s\n", p->get_guid(), t, f, t, project_platform); + } + IO::print(out, "\tEndGlobalSection\n"); + IO::print(out, "EndGlobal\n"); + + return true; +} diff --git a/plugins/builtin/vssolutiongenerator.h b/plugins/builtin/vssolutiongenerator.h new file mode 100644 index 0000000..c98fcb2 --- /dev/null +++ b/plugins/builtin/vssolutiongenerator.h @@ -0,0 +1,19 @@ +#ifndef VSSOLUTIONGENERATOR_H_ +#define VSSOLUTIONGENERATOR_H_ + +#include + +class VsSolutionFile; + +class VsSolutionGenerator: public Tool +{ +public: + VsSolutionGenerator(Builder &); + + Target *create_target(const std::vector &, const std::string &) override; + +private: + static bool _run(const VsSolutionFile &); +}; + +#endif diff --git a/plugins/clang/clangcompiler.cpp b/plugins/clang/clangcompiler.cpp new file mode 100644 index 0000000..bcd73d1 --- /dev/null +++ b/plugins/clang/clangcompiler.cpp @@ -0,0 +1,10 @@ +#include "clangcompiler.h" + +using namespace std; +using namespace Msp; + +ClangCompiler::ClangCompiler(Builder &b, const Architecture &a, const string &t): + CustomizedTool(b, t, a) +{ + set_command((tag=="CXX" ? "clang++" : "clang"), true); +} diff --git a/plugins/clang/clangcompiler.h b/plugins/clang/clangcompiler.h new file mode 100644 index 0000000..034b636 --- /dev/null +++ b/plugins/clang/clangcompiler.h @@ -0,0 +1,12 @@ +#ifndef CLANGCOMPILER_H_ +#define CLANGCOMPILER_H_ + +#include + +class ClangCompiler: public CustomizedTool +{ +public: + ClangCompiler(Builder &, const Architecture &, const std::string &); +}; + +#endif diff --git a/plugins/clang/clanglinker.cpp b/plugins/clang/clanglinker.cpp new file mode 100644 index 0000000..fb15451 --- /dev/null +++ b/plugins/clang/clanglinker.cpp @@ -0,0 +1,24 @@ +#include +#include +#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(tool).get_tag(), p); + tool.system_path.push_back(p); + break; + } +} diff --git a/plugins/clang/clanglinker.h b/plugins/clang/clanglinker.h new file mode 100644 index 0000000..a6518b1 --- /dev/null +++ b/plugins/clang/clanglinker.h @@ -0,0 +1,15 @@ +#ifndef CLANGLINKER_H_ +#define CLANGLINKER_H_ + +#include + +class ClangLinker: public CustomizedTool +{ +public: + ClangLinker(Builder &, const Architecture &); + +protected: + void do_prepare(ToolData &) const override; +}; + +#endif diff --git a/plugins/clang/clangtools.cpp b/plugins/clang/clangtools.cpp new file mode 100644 index 0000000..385a279 --- /dev/null +++ b/plugins/clang/clangtools.cpp @@ -0,0 +1,26 @@ +#include +#include "clangcompiler.h" +#include "clanglinker.h" +#include "clangtools.h" + +using namespace std; + +ClangTools::ClangTools(Builder &builder, const Architecture &arch): + Toolchain("clang", get_priority(arch)) +{ + add_tool(new ClangCompiler(builder, arch, "CC")); + add_tool(new ClangCompiler(builder, arch, "CXX")); + add_tool(new ClangCompiler(builder, arch, "OBJC")); + + add_tool(new ClangLinker(builder, arch)); +} + +int ClangTools::get_priority(const Architecture &arch) +{ + if(arch.get_toolchain()=="clang") + return 20; + else if(arch.get_system()=="darwin" || arch.get_system()=="freebsd") + return 10; + else + return 0; +} diff --git a/plugins/clang/clangtools.h b/plugins/clang/clangtools.h new file mode 100644 index 0000000..971404a --- /dev/null +++ b/plugins/clang/clangtools.h @@ -0,0 +1,17 @@ +#ifndef CLANGTOOLS_H_ +#define CLANGTOOLS_H_ + +#include + +class Architecture; +class Builder; + +class ClangTools: public Toolchain +{ +public: + ClangTools(Builder &, const Architecture &); + + static int get_priority(const Architecture &); +}; + +#endif diff --git a/plugins/datafile/datacollection.cpp b/plugins/datafile/datacollection.cpp new file mode 100644 index 0000000..189eb61 --- /dev/null +++ b/plugins/datafile/datacollection.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include "datacollection.h" +#include "datatransform.h" + +using namespace Msp; + +DataCollection::DataCollection(Builder &b, const Component &c, DataTransform &s): + FileTarget(b, c.get_package(), generate_target_path(c, s.get_path())), + source(s) +{ + component = &c; + add_dependency(source); +} + +Msp::FS::Path DataCollection::generate_target_path(const Component &comp, const Msp::FS::Path &src) +{ + return comp.get_package().get_temp_directory()/comp.get_name()/(FS::basepart(FS::basename(src))+".mdc"); +} + +void DataCollection::find_dependencies() +{ + source.prepare(); + for(Target *d: source.get_transitive_dependencies()) + add_dependency(*d); +} diff --git a/plugins/datafile/datacollection.h b/plugins/datafile/datacollection.h new file mode 100644 index 0000000..2fca294 --- /dev/null +++ b/plugins/datafile/datacollection.h @@ -0,0 +1,26 @@ +#ifndef DATACOLLECTION_H_ +#define DATACOLLECTION_H_ + +#include + +class DataTransform; + +class DataCollection: public FileTarget +{ +private: + DataTransform &source; + +public: + DataCollection(Builder &, const Component &, DataTransform &); +private: + static Msp::FS::Path generate_target_path(const Component &, const Msp::FS::Path &); + +public: + const char *get_type() const override { return "DataCollection"; } + DataTransform &get_source() const { return source; } + +private: + void find_dependencies() override; +}; + +#endif diff --git a/plugins/datafile/datapack.cpp b/plugins/datafile/datapack.cpp new file mode 100644 index 0000000..2abcd52 --- /dev/null +++ b/plugins/datafile/datapack.cpp @@ -0,0 +1,21 @@ +#include +#include +#include "datapack.h" + +using namespace std; + +DataPack::DataPack(Builder &b, const Component &c, const vector &f): + FileTarget(b, c.get_package(), generate_target_path(c)), + files(f) +{ + component = &c; + for(FileTarget *t: files) + add_dependency(*t); + + install_location = Msp::FS::Path("share")/package->get_name(); +} + +Msp::FS::Path DataPack::generate_target_path(const Component &comp) +{ + return comp.get_package().get_output_directory()/(comp.get_name()+".mdp"); +} diff --git a/plugins/datafile/datapack.h b/plugins/datafile/datapack.h new file mode 100644 index 0000000..80a1b0d --- /dev/null +++ b/plugins/datafile/datapack.h @@ -0,0 +1,22 @@ +#ifndef DATAPACK_H_ +#define DATAPACK_H_ + +#include + +class DataPack: public FileTarget +{ +private: + std::vector files; + +public: + DataPack(Builder &, const Component &, const std::vector &); +private: + static Msp::FS::Path generate_target_path(const Component &); + +public: + const char *get_type() const override { return "DataPack"; } + + const std::vector &get_files() const { return files; } +}; + +#endif diff --git a/plugins/datafile/datapackcomponent.cpp b/plugins/datafile/datapackcomponent.cpp new file mode 100644 index 0000000..9b628f4 --- /dev/null +++ b/plugins/datafile/datapackcomponent.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#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 files; + for(const FS::Path &s: collect_source_files()) + { + string ext = FS::extpart(FS::basename(s)); + if(ext==".mdt") + { + Target *src = dcomp.create_source(*this, s); + files.push_back(dcomp.create_target(*src, "collection")); + } + else if(Target *tgt = builder.get_vfs().get_target(s)) + files.push_back(tgt); + else + files.push_back(new DataSourceFile(builder, *this, s)); + } + + Target *result = dcomp.create_target(files, "pack"); + + BuildGraph &build_graph = builder.get_build_graph(); + build_graph.add_primary_target(*result); + if(install) + build_graph.add_installed_target(*result); +} diff --git a/plugins/datafile/datapackcomponent.h b/plugins/datafile/datapackcomponent.h new file mode 100644 index 0000000..3729922 --- /dev/null +++ b/plugins/datafile/datapackcomponent.h @@ -0,0 +1,14 @@ +#ifndef DATAPACKCOMPONENT_H_ +#define DATAPACKCOMPONENT_H_ + +#include + +class DataPackComponent: public Component +{ +public: + DataPackComponent(SourcePackage &, const std::string &); + + void create_targets() const override; +}; + +#endif diff --git a/plugins/datafile/datasourcefile.h b/plugins/datafile/datasourcefile.h new file mode 100644 index 0000000..8d2fcd5 --- /dev/null +++ b/plugins/datafile/datasourcefile.h @@ -0,0 +1,15 @@ +#ifndef DATASOURCEFILE_H_ +#define DATASOURCEFILE_H_ + +#include + +class DataSourceFile: public SourceFile +{ +public: + DataSourceFile(Builder &b, const Msp::FS::Path &p): SourceFile(b, p) { } + DataSourceFile(Builder &b, const Component &c, const Msp::FS::Path &p): SourceFile(b, c, p) { } + + const char *get_type() const override { return "DataSourceFile"; } +}; + +#endif diff --git a/plugins/datafile/datatool.cpp b/plugins/datafile/datatool.cpp new file mode 100644 index 0000000..04592e3 --- /dev/null +++ b/plugins/datafile/datatool.cpp @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include +#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 &sources, const string &arg) +{ + if(arg=="collection") + { + if(sources.size()!=1) + throw invalid_argument("DataTool::create_target"); + DataTransform &source = dynamic_cast(*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 files; + files.reserve(sources.size()); + for(Target *t: sources) + files.push_back(&dynamic_cast(*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 argv; + argv.push_back(tool.get_executable()->get_path().str()); + + argv.push_back("-o"); + argv.push_back(FS::relative(dynamic_cast(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(&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(&tgt)) + { + argv.push_back("-p"); + for(const FileTarget *f: pack->get_files()) + argv.push_back(FS::relative(f->get_path(), work_dir).str()); + } + + return new ExternalTask(argv, work_dir); +} diff --git a/plugins/datafile/datatool.h b/plugins/datafile/datatool.h new file mode 100644 index 0000000..a76f7f1 --- /dev/null +++ b/plugins/datafile/datatool.h @@ -0,0 +1,19 @@ +#ifndef DATACOMPILER_H_ +#define DATACOMPILER_H_ + +#include + +class DataTool: public Tool +{ +public: + DataTool(Builder &); + + Target *create_source(const Component &, const Msp::FS::Path &) const override; + Target *create_target(const std::vector &, const std::string &) override; + std::string create_build_signature(const BuildInfo &) const override; + +private: + static Task *_run(const Target &); +}; + +#endif diff --git a/plugins/datafile/datatransform.cpp b/plugins/datafile/datatransform.cpp new file mode 100644 index 0000000..c462ce2 --- /dev/null +++ b/plugins/datafile/datatransform.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 files; + Cache &cache = component->get_package().get_cache(); + const Time::TimeStamp &cache_mtime = cache.get_mtime(); + if(mtime 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()); + 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()); + } + + cache.set_values(this, "files", files); + } + + for(const string &f: files) + { + FS::Path file_path = FS::dirname(path)/f; + if(Target *tgt = builder.get_vfs().get_target(file_path)) + add_transitive_dependency(*tgt); + else + add_transitive_dependency(*new File(builder, *package, file_path)); + } +} diff --git a/plugins/datafile/datatransform.h b/plugins/datafile/datatransform.h new file mode 100644 index 0000000..9b11e61 --- /dev/null +++ b/plugins/datafile/datatransform.h @@ -0,0 +1,20 @@ +#ifndef DATATRANSFORM_H_ +#define DATATRANSFORM_H_ + +#include + +class DataTransform: public FileTarget +{ +private: + Msp::Time::TimeStamp dir_mtime; + +public: + DataTransform(Builder &, const Component &, const Msp::FS::Path &); + + const char *get_type() const override { return "DataTransform"; } + +private: + void find_dependencies() override; +}; + +#endif diff --git a/plugins/gnu/gnuarchiver.cpp b/plugins/gnu/gnuarchiver.cpp new file mode 100644 index 0000000..c977eaa --- /dev/null +++ b/plugins/gnu/gnuarchiver.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#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 &sources, const string &) +{ + if(sources.empty()) + throw invalid_argument("GnuArchiver::create_target"); + + vector objs; + objs.reserve(sources.size()); + for(Target *s: sources) + objs.push_back(&dynamic_cast(*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 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(d)) + argv.push_back(relative(obj->get_path(), work_dir).str()); + + return argv; +} diff --git a/plugins/gnu/gnuarchiver.h b/plugins/gnu/gnuarchiver.h new file mode 100644 index 0000000..81ed47e --- /dev/null +++ b/plugins/gnu/gnuarchiver.h @@ -0,0 +1,19 @@ +#ifndef GNUARCHIVER_H_ +#define GNUARCHIVER_H_ + +#include + +class StaticLibrary; + +class GnuArchiver: public Tool +{ +public: + GnuArchiver(Builder &, const Architecture &); + + Target *create_target(const std::vector &, const std::string &) override; + +private: + static ExternalTask::Arguments _run(const StaticLibrary &, Msp::FS::Path &); +}; + +#endif diff --git a/plugins/gnu/gnucompiler.cpp b/plugins/gnu/gnucompiler.cpp new file mode 100644 index 0000000..ba74409 --- /dev/null +++ b/plugins/gnu/gnucompiler.cpp @@ -0,0 +1,353 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 &sources, const string &) +{ + if(sources.size()!=1) + throw invalid_argument("GnuCompiler::create_target"); + SourceFile &source = dynamic_cast(*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).get_tag(); + + const FileTarget *exe = static_cast(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 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).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).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 version_parts = split(version_str, '.'); + for(unsigned i=0; (i<3 && i(version_parts[i])<<(16-8*i); + } + catch(const runtime_error &) + { } + + return ver; +} + +ExternalTask::Arguments GnuCompiler::_run(const ObjectFile &object, FS::Path &work_dir) +{ + const Tool &tool = *object.get_tool(); + const Architecture &arch = *tool.get_architecture(); + + ExternalTask::Arguments argv; + argv.push_back(tool.get_executable()->get_path().str()); + argv.push_back("-c"); + + BuildInfo binfo; + object.collect_build_info(binfo); + + const std::string &tool_tag = tool.get_tag(); + string tag_for_std = (tool_tag=="OBJC" ? "CC" : tool_tag); + if(binfo.standards.count(tag_for_std)) + argv.push_back("-std="+get_item(binfo.standards, tag_for_std).str()); + if(tool_tag=="OBJC" && binfo.standards.count(tool_tag)) + argv.push_back("-fobjc-std="+get_item(binfo.standards, tool_tag).str()); + + if(binfo.warning_level>=1) + { + argv.push_back("-Wall"); + if(binfo.warning_level>=2) + { + argv.push_back("-Wextra"); + argv.push_back("-Wundef"); + unsigned version = tool.get_extra_data(); + if(version>=0x80000) + argv.push_back("-Wno-cast-function-type"); + } + if(binfo.warning_level>=3) + { + argv.push_back("-pedantic"); + argv.push_back("-Wno-long-long"); + argv.push_back("-Wshadow"); + if(tool_tag=="CC") + { + argv.push_back("-Wc++-compat"); + argv.push_back("-Wstrict-prototypes"); + } + } + if(binfo.warning_level>=4) + { + // Some truly paranoid warnings + argv.push_back("-Wstrict-overflow=4"); + argv.push_back("-Wfloat-equal"); + argv.push_back("-Wconversion"); + argv.push_back("-Wwrite-strings"); + argv.push_back("-Winline"); + } + if(binfo.fatal_warnings) + { + argv.push_back("-Werror"); + argv.push_back("-Wno-error=deprecated-declarations"); + } + } + + const FS::Path &sysroot = binfo.sysroot; + if(!sysroot.empty()) + argv.push_back("--sysroot="+sysroot.str()); + for(const FS::Path &p: binfo.local_incpath) + { + argv.push_back("-iquote"); + argv.push_back(p.str()); + } + for(const FS::Path &p: binfo.incpath) + argv.push_back("-I"+p.str()); + + for(const auto &kvp: binfo.defines) + { + if(kvp.second.empty()) + argv.push_back(format("-D%s", kvp.first)); + else + argv.push_back(format("-D%s=%s", kvp.first, kvp.second)); + } + + if(binfo.debug) + argv.push_back("-ggdb"); + if(binfo.optimize) + { + if(binfo.optimize<0) + argv.push_back("-Os"); + else + argv.push_back(format("-O%d", binfo.optimize)); + if(binfo.debug) + argv.push_back("-fno-omit-frame-pointer"); + } + if(binfo.threads && arch.get_system()!="windows" && arch.get_system()!="darwin") + argv.push_back("-pthread"); + if(object.is_used_in_shared_library() && arch.get_system()!="windows") + argv.push_back("-fPIC"); + + if((arch.get_type()=="x86" || arch.get_type()=="ppc") && !arch.is_native()) + argv.push_back(format("-m%d", arch.get_bits())); + + string cpu = arch.get_cpu(); + if(!cpu.empty()) + { + for(unsigned i=0; cpus[i]; i+=2) + if(cpu==cpus[i]) + { + cpu = cpus[i+1]; + break; + } + argv.push_back("-march="+cpu); + } + + if(!arch.get_fpu().empty()) + { + if(arch.get_type()=="x86") + { + if(arch.get_fpu()=="387") + argv.push_back("-mfpmath=387"); + else if(!arch.get_fpu().compare(0, 3, "sse")) + argv.push_back("-mfpmath=sse"); + + if(arch.get_fpu()=="sse") + argv.push_back("-msse2"); + else if(arch.get_fpu()=="sse3") + argv.push_back("-msse3"); + else if(arch.get_fpu()=="sse4.1") + argv.push_back("-msse4.1"); + } + else if(arch.get_type()=="arm") + { + argv.push_back("-mfpu="+arch.get_fpu()); + argv.push_back("-mfloat-abi=softfp"); + } + } + + FS::Path obj_path = object.get_path(); + FS::Path src_path = object.get_source().get_path(); + + argv.push_back("-o"); + argv.push_back(relative(obj_path, work_dir).str()); + argv.push_back(relative(src_path, work_dir).str()); + + return argv; +} diff --git a/plugins/gnu/gnucompiler.h b/plugins/gnu/gnucompiler.h new file mode 100644 index 0000000..98c71ed --- /dev/null +++ b/plugins/gnu/gnucompiler.h @@ -0,0 +1,34 @@ +#ifndef GNUCOMPILER_H_ +#define GNUCOMPILER_H_ + +#include + +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 &, const std::string &) override; + std::string create_build_signature(const BuildInfo &) const override; +protected: + void do_prepare(ToolData &) const override; + void prepare_syspath(ToolData &) const; + void prepare_version(ToolData &) const; + unsigned query_version(const std::string &, const std::string &) const; + +private: + static ExternalTask::Arguments _run(const ObjectFile &, Msp::FS::Path &); +}; + +#endif diff --git a/plugins/gnu/gnulinker.cpp b/plugins/gnu/gnulinker.cpp new file mode 100644 index 0000000..710c44b --- /dev/null +++ b/plugins/gnu/gnulinker.cpp @@ -0,0 +1,325 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 &sources, const string &arg) +{ + if(sources.empty()) + throw invalid_argument("GnuLinker::create_target"); + vector objs; + objs.reserve(sources.size()); + for(Target *s: sources) + objs.push_back(&dynamic_cast(*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(&target)) + { + Tool © = builder.get_toolchain().get_tool("CP"); + InstalledFile *inst_tgt = dynamic_cast(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(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).get_tag(); + + const FileTarget *exe = static_cast(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(startis_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(&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(d); + Target *tgt = d->get_real_target(); + + if(ObjectFile *obj = dynamic_cast(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(tgt)) + argv.push_back((file?file:stlib)->get_path().str()); + else if(SharedLibrary *shlib = dynamic_cast(tgt)) + { + argv.push_back("-l"+shlib->get_libname()); + static_link_ok = false; + } + else if(ImportLibrary *imp = dynamic_cast(tgt)) + { + shlib = imp->get_shared_library(); + if(shlib) + argv.push_back("-l"+shlib->get_libname()); + else + argv.push_back((file?file:imp)->get_path().str()); + static_link_ok = false; + } + } + + for(const string &l: binfo.libs) + if(l.size()>10 && !l.compare(l.size()-10, 10, ".framework")) + { + argv.push_back("-framework"); + argv.push_back(l.substr(0, l.size()-10)); + } + + if(static_link_ok) + argv.push_back("-static"); + else + { + if(has_cplusplus) + { + auto i = binfo.libmodes.find("stdc++"); + if(i!=binfo.libmodes.end() && i->second<=BuildInfo::STATIC) + argv.push_back("-static-libstdc++"); + } + + if(arch.get_system()=="windows") + argv.push_back("-Wl,--enable-auto-import"); + } + + return argv; +} diff --git a/plugins/gnu/gnulinker.h b/plugins/gnu/gnulinker.h new file mode 100644 index 0000000..11a0fb1 --- /dev/null +++ b/plugins/gnu/gnulinker.h @@ -0,0 +1,29 @@ +#ifndef GNULINKER_H_ +#define GNULINKER_H_ + +#include + +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 &, const std::string &) override; + Target *create_install(Target &) const override; + std::string create_build_signature(const BuildInfo &) const override; +protected: + void do_prepare(ToolData &) const override; +private: + static ExternalTask::Arguments _run(const Binary &, Msp::FS::Path &); +}; + +#endif diff --git a/plugins/gnu/gnutools.cpp b/plugins/gnu/gnutools.cpp new file mode 100644 index 0000000..14c9f11 --- /dev/null +++ b/plugins/gnu/gnutools.cpp @@ -0,0 +1,30 @@ +#include +#include "gnuarchiver.h" +#include "gnucompiler.h" +#include "gnulinker.h" +#include "gnutools.h" +#include "mingwdlltool.h" + +GnuTools::GnuTools(Builder &builder, const Architecture &arch): + Toolchain("gnu", get_priority(arch)) +{ + add_tool(new GnuCompiler(builder, arch, "CC")); + add_tool(new GnuCompiler(builder, arch, "CXX")); + add_tool(new GnuCompiler(builder, arch, "OBJC")); + + add_tool(new GnuLinker(builder, arch)); + add_tool(new GnuArchiver(builder, arch)); + + if(arch.get_system()=="windows") + add_tool(new MingwDllTool(builder, arch)); +} + +int GnuTools::get_priority(const Architecture &arch) +{ + if(arch.get_toolchain()=="gnu") + return 20; + else if(arch.get_system()=="linux") + return 10; + else + return 0; +} diff --git a/plugins/gnu/gnutools.h b/plugins/gnu/gnutools.h new file mode 100644 index 0000000..59e2908 --- /dev/null +++ b/plugins/gnu/gnutools.h @@ -0,0 +1,17 @@ +#ifndef GNUTOOLS_H_ +#define GNUTOOLS_H_ + +#include + +class Architecture; +class Builder; + +class GnuTools: public Toolchain +{ +public: + GnuTools(Builder &, const Architecture &); + + static int get_priority(const Architecture &); +}; + +#endif diff --git a/plugins/gnu/mingwdlltool.cpp b/plugins/gnu/mingwdlltool.cpp new file mode 100644 index 0000000..c5ffdf5 --- /dev/null +++ b/plugins/gnu/mingwdlltool.cpp @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 &sources, const string &) +{ + if(sources.size()!=1) + throw invalid_argument("MingwDllTool::create_target"); + SharedLibrary &shlib = dynamic_cast(*sources.front()); + + vector objs; + objs.reserve(shlib.get_dependencies().size()); + for(Target *d: shlib.get_dependencies()) + if(ObjectFile *obj = dynamic_cast(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(&target)) + { + Tool © = builder.get_toolchain().get_tool("CP"); + InstalledFile *inst_tgt = dynamic_cast(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(&target); + const ExportDefinitions *exp = 0; + if(imp) + exp = &dynamic_cast(*imp->get_dependencies().front()); + else + exp = dynamic_cast(&target); + if(!imp && !exp) + throw invalid_argument("MingwDllTool::run"); + + vector 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(d)) + argv.push_back(relative(obj->get_path(), work_dir).str()); + + // XXX Should use dllexport, but that has some other problems to solve + argv.push_back("--export-all-symbols"); + + argv.push_back("-z"); + argv.push_back(relative(exp->get_path(), work_dir).str()); + } + + return new ExternalTask(argv, work_dir); +} diff --git a/plugins/gnu/mingwdlltool.h b/plugins/gnu/mingwdlltool.h new file mode 100644 index 0000000..d35fa19 --- /dev/null +++ b/plugins/gnu/mingwdlltool.h @@ -0,0 +1,18 @@ +#ifndef DLLTOOL_H_ +#define DLLTOOL_H_ + +#include + +class MingwDllTool: public Tool +{ +public: + MingwDllTool(Builder &, const Architecture &); + + Target *create_target(const std::vector &, const std::string &) override; + Target *create_install(Target &) const override; + +private: + static Task *_run(const Target &); +}; + +#endif diff --git a/plugins/msvc/microsofttools.cpp b/plugins/msvc/microsofttools.cpp new file mode 100644 index 0000000..af435bb --- /dev/null +++ b/plugins/msvc/microsofttools.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include +#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 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("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 sdk_versions = FS::list_filtered(win_sdk_dir/"include", "^10\\."); + if(sdk_versions.empty()) + { + builder.get_logger().log("problems", "No Windows SDK versions found"); + return; + } + + sort(sdk_versions); + win_sdk_version = sdk_versions.back(); + + builder.get_logger().log("tools", "Windows SDK version %s found in %s", win_sdk_version, win_sdk_dir); +} + +int MicrosoftTools::get_priority(const Architecture &arch) +{ + if(arch.get_toolchain()=="msvc") + return 20; + else if(arch.get_system()=="windows") + return 10; + else + return 0; +} diff --git a/plugins/msvc/microsofttools.h b/plugins/msvc/microsofttools.h new file mode 100644 index 0000000..900bb68 --- /dev/null +++ b/plugins/msvc/microsofttools.h @@ -0,0 +1,34 @@ +#ifndef MICROSOFTTOOLS_H_ +#define MICROSOFTTOOLS_H_ + +#include +#include + +class Architecture; +class Builder; + +class MicrosoftTools: public Toolchain +{ +private: + Msp::FS::Path vc_base_dir; + Msp::FS::Path vc_bin_dir; + Msp::FS::Path win_sdk_dir; + std::string win_sdk_version; + +public: + MicrosoftTools(Builder &, const Architecture &); + +private: + void find_vc_bin_dir(Builder &, const Architecture &); + void find_windows_sdk_dir(Builder &); + +public: + const Msp::FS::Path &get_vc_base_dir() const { return vc_base_dir; } + const Msp::FS::Path &get_vc_bin_dir() const { return vc_bin_dir; } + const Msp::FS::Path &get_windows_sdk_dir() const { return win_sdk_dir; } + const std::string &get_windows_sdk_version() const { return win_sdk_version; } + + static int get_priority(const Architecture &); +}; + +#endif diff --git a/plugins/msvc/msvcarchiver.cpp b/plugins/msvc/msvcarchiver.cpp new file mode 100644 index 0000000..adcb8e7 --- /dev/null +++ b/plugins/msvc/msvcarchiver.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#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 &sources, const string &) +{ + if(sources.empty()) + throw invalid_argument("MsvcArchiver::create_target"); + + vector objs; + objs.reserve(sources.size()); + for(Target *s: sources) + objs.push_back(&dynamic_cast(*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 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(d)) + argv.push_back(relative(obj->get_path(), work_dir).str()); + + return argv; +} diff --git a/plugins/msvc/msvcarchiver.h b/plugins/msvc/msvcarchiver.h new file mode 100644 index 0000000..89d24c6 --- /dev/null +++ b/plugins/msvc/msvcarchiver.h @@ -0,0 +1,23 @@ +#ifndef MSVCARCHIVER_H_ +#define MSVCARCHIVER_H_ + +#include + +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 &, const std::string &) override; + +private: + static ExternalTask::Arguments _run(const StaticLibrary &, Msp::FS::Path &); +}; + +#endif diff --git a/plugins/msvc/msvccompiler.cpp b/plugins/msvc/msvccompiler.cpp new file mode 100644 index 0000000..2e63335 --- /dev/null +++ b/plugins/msvc/msvccompiler.cpp @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 &sources, const string &) +{ + if(sources.size()!=1) + throw invalid_argument("MsvcCompiler::create_target"); + SourceFile &source = dynamic_cast(*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).get_tag(); + + const FS::Path &vc_base_dir = ms_tools.get_vc_base_dir(); + tool.system_path.push_back(vc_base_dir/"include"); + + const FS::Path &win_sdk_dir = ms_tools.get_windows_sdk_dir(); + const string &win_sdk_ver = ms_tools.get_windows_sdk_version(); + tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"ucrt"); + tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"shared"); + tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"um"); + + string path; + for(const FS::Path &p: tool.system_path) + { + append(path, ";", p.str()); + builder.get_logger().log("tools", "Got %s system path: %s", tool_tag, p); + } + + setenv("INCLUDE", path); +} + +ExternalTask::Arguments MsvcCompiler::_run(const ObjectFile &object, FS::Path &work_dir) +{ + const Tool &tool = *object.get_tool(); + + ExternalTask::Arguments argv; + argv.push_back(tool.get_executable()->get_path().str()); + argv.push_back("/nologo"); + argv.push_back("/c"); + + BuildInfo binfo; + object.collect_build_info(binfo); + + if(binfo.standards.count(tool.get_tag())) + { + const BuildInfo::LanguageStandard &std = get_item(binfo.standards, tool.get_tag()); + if((tool.get_tag()=="CXX" && std.year>2011) || (tool.get_tag()=="CC" && std.year>1999)) + argv.push_back("/std:"+std.str()); + } + + if(binfo.warning_level>=1) + { + argv.push_back(format("/W%d", binfo.warning_level)); + if(binfo.fatal_warnings) + argv.push_back("/WX"); + if(binfo.warning_level>=3) + argv.push_back("/permissive-"); + + argv.push_back("/wd4068"); // Unknown pragma + if(binfo.warning_level<4) + { + argv.push_back("/wd4244"); // Narrowing conversion on arg or return + argv.push_back("/wd4267"); // Narrowing conversion + } + } + else + argv.push_back("/w"); + + for(const FS::Path &p: binfo.local_incpath) + { + argv.push_back("/I"); + argv.push_back(p.str()); + } + for(const FS::Path &p: binfo.incpath) + { + argv.push_back("/I"); + argv.push_back(p.str()); + } + + for(const auto &kvp: binfo.defines) + { + argv.push_back("/D"); + if(kvp.second.empty()) + argv.push_back(kvp.first); + else + argv.push_back(format("%s=%s", kvp.first, kvp.second)); + } + + if(binfo.debug) + argv.push_back("/Z7"); + if(binfo.optimize) + { + if(binfo.optimize<0) + argv.push_back("/O1"); + else if(binfo.optimize==1) + argv.push_back("/Ox"); + else + argv.push_back("/O2"); + } + + if(binfo.libmode<=BuildInfo::STATIC) + argv.push_back(binfo.debug ? "/MTd" : "/MT"); + else + argv.push_back(binfo.debug ? "/MDd" : "/MD"); + + argv.push_back("/EHsc"); + + FS::Path obj_path = object.get_path(); + FS::Path src_path = object.get_source().get_path(); + + argv.push_back("/Fo"+relative(obj_path, work_dir).str()); + argv.push_back(relative(src_path, work_dir).str()); + + return argv; +} diff --git a/plugins/msvc/msvccompiler.h b/plugins/msvc/msvccompiler.h new file mode 100644 index 0000000..ef60e2e --- /dev/null +++ b/plugins/msvc/msvccompiler.h @@ -0,0 +1,29 @@ +#ifndef MSVCCOMPILER_H_ +#define MSVCCOMPILER_H_ + +#include + +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 &, const std::string &) override; + std::string create_build_signature(const BuildInfo &) const override; + +protected: + void do_prepare(ToolData &) const override; + +public: + static ExternalTask::Arguments _run(const ObjectFile &, Msp::FS::Path &); +}; + +#endif diff --git a/plugins/msvc/msvclinker.cpp b/plugins/msvc/msvclinker.cpp new file mode 100644 index 0000000..8008791 --- /dev/null +++ b/plugins/msvc/msvclinker.cpp @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 &sources, const string &arg) +{ + if(sources.empty()) + throw invalid_argument("MsvcLinker::create_target"); + + vector objs; + objs.reserve(sources.size()); + for(Target *s: sources) + objs.push_back(&dynamic_cast(*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).get_tag(); + const Architecture &arch = *static_cast(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 argv; + argv.push_back(tool.get_executable()->get_path().str()); + argv.push_back("/NOLOGO"); + + if(dynamic_cast(&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(d); + Target *tgt = d->get_real_target(); + + if(ObjectFile *obj = dynamic_cast(tgt)) + argv.push_back(relative(obj->get_path(), work_dir).str()); + else if(StaticLibrary *stlib = dynamic_cast(tgt)) + argv.push_back((file?file:stlib)->get_path().str()); + else if(ImportLibrary *imp = dynamic_cast(tgt)) + argv.push_back((file?file:imp)->get_path().str()); + } + + argv.push_back("/SUBSYSTEM:CONSOLE"); + + return argv; +} diff --git a/plugins/msvc/msvclinker.h b/plugins/msvc/msvclinker.h new file mode 100644 index 0000000..fe98c7d --- /dev/null +++ b/plugins/msvc/msvclinker.h @@ -0,0 +1,27 @@ +#ifndef MSVCLINKER_H_ +#define MSVCLINKER_H_ + +#include + +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 &, const std::string &) override; + std::string create_build_signature(const BuildInfo &) const override; + +protected: + void do_prepare(ToolData &data) const override; + +public: + static ExternalTask::Arguments _run(const Binary &, Msp::FS::Path &); +}; + +#endif diff --git a/source/analyzer.cpp b/source/analyzer.cpp deleted file mode 100644 index f91c298..0000000 --- a/source/analyzer.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#include -#include -#include -#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(&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(&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 &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 col_width; - - // Determine column widths - for(const vector &r: table) - { - if(col_width.size() &r: table) - { - string line; - for(unsigned j=0; j0) - line += " "; - line += lexical_cast(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()get_name(); -} - -bool Analyzer::target_order_full(const Target *t1, const Target *t2) -{ - const FileTarget *ft1 = dynamic_cast(t1); - const FileTarget *ft2 = dynamic_cast(t2); - if(!ft1) - { - if(ft2) - return true; - return target_order(t1, t2); - } - else if(!ft2) - return false; - return ft1->get_path().str()get_path().str(); -} diff --git a/source/analyzer.h b/source/analyzer.h deleted file mode 100644 index d30de04..0000000 --- a/source/analyzer.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef ANALYZER_H_ -#define ANALYZER_H_ - -#include -#include -#include -#include - -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; - - Builder &builder; - Mode mode = DEPS; - std::vector table; - unsigned max_depth = 0; - bool full_paths = false; - std::map > rdepends; - -public: - Analyzer(Builder &b): builder(b) { } - - void set_mode(Mode m) { mode = m; } - void set_max_depth(unsigned m) { max_depth = m; } - void set_full_paths(bool f) { full_paths = f; } - - /// Performs the analysis and prints out the resulting dependency tree. - void analyze(); - -private: - /** Adds rows for a target, then recursively adds rows for dependencies as - needed. */ - void build_depend_table(Target &, unsigned); - - /// Prints out the table that resulted from the analysis. - void print_table() const; - - static bool target_order(const Target *, const Target *); - static bool target_order_full(const Target *, const Target *); -}; - -#endif diff --git a/source/androidapplicationcomponent.cpp b/source/androidapplicationcomponent.cpp deleted file mode 100644 index e4feaa2..0000000 --- a/source/androidapplicationcomponent.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#include -#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 contents; - for(const auto &kvp: build_graph.get_targets()) - if(kvp.second->get_package()==&package) - if(InstalledFile *inst = dynamic_cast(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 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 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(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(c) -{ - add("orientation", &AndroidApplicationComponent::orientation); - add("permission", &Loader::permission); -} - -void AndroidApplicationComponent::Loader::permission(const string &perm) -{ - if(!any_equals(obj.permissions, perm)) - obj.permissions.push_back(perm); -} diff --git a/source/androidapplicationcomponent.h b/source/androidapplicationcomponent.h deleted file mode 100644 index 9e19b6d..0000000 --- a/source/androidapplicationcomponent.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef ANDROIDAPPLICATIONCOMPONENT_H_ -#define ANDROIDAPPLICATIONCOMPONENT_H_ - -#include "component.h" - -class AndroidApplicationComponent: public Component -{ -public: - class Loader: public Msp::DataFile::DerivedObjectLoader - { - public: - Loader(AndroidApplicationComponent &); - - private: - void permission(const std::string &); - }; - -private: - std::string orientation; - std::vector permissions; - -public: - AndroidApplicationComponent(SourcePackage &p, const std::string &n): Component(p, n) { } - - void create_targets() const override; -}; - -#endif diff --git a/source/androidarchiver.cpp b/source/androidarchiver.cpp deleted file mode 100644 index 617445c..0000000 --- a/source/androidarchiver.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "androidarchiver.h" -#include "androidtools.h" - -AndroidArchiver::AndroidArchiver(Builder &b, const Architecture &a, const AndroidNdk &ndk): - CustomizedTool(b, "AR", a) -{ - set_command("ar", true); - if(ndk.get_root_dir().empty()) - problems.push_back("Android NDK not found"); - else if(ndk.get_bin_dir().empty()) - problems.push_back("Android NDK toolchain not found"); - else - set_command((ndk.get_bin_dir()/command).str()); -} diff --git a/source/androidarchiver.h b/source/androidarchiver.h deleted file mode 100644 index a39fe54..0000000 --- a/source/androidarchiver.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef ANDROIDARCHIVER_H_ -#define ANDROIDARCHIVER_H_ - -#include "customizedtool.h" - -class AndroidNdk; - -class AndroidArchiver: public CustomizedTool -{ -public: - AndroidArchiver(Builder &, const Architecture &, const AndroidNdk &); -}; - -#endif diff --git a/source/androidassetpackagingtool.cpp b/source/androidassetpackagingtool.cpp deleted file mode 100644 index eb638e9..0000000 --- a/source/androidassetpackagingtool.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#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 &sources, const string &) -{ - AndroidManifestFile *manifest = 0; - vector resources; - resources.reserve(sources.size()); - for(Target *s: sources) - { - if(AndroidManifestFile *m = dynamic_cast(s)) - manifest = m; - else if(FileTarget *f = dynamic_cast(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(*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 resource_dirs; - resource_dirs.reserve(res.get_dependencies().size()); - for(Target *d: res.get_dependencies()) - { - FileTarget *file = dynamic_cast(d); - Target *real = d->get_real_target(); - - if(dynamic_cast(real)) - { - argv.push_back("-M"); - argv.push_back(FS::relative(file->get_path(), work_dir).str()); - } - else if(real->get_package()==res.get_package()) - { - const FS::Path &path = file->get_path(); - FS::Path res_dir = path.subpath(0, path.size()-2); - if(!any_equals(resource_dirs, res_dir)) - resource_dirs.push_back(res_dir); - } - } - - for(const FS::Path &d: resource_dirs) - { - argv.push_back("-S"); - argv.push_back(FS::relative(d, work_dir).str()); - } - - return argv; -} diff --git a/source/androidassetpackagingtool.h b/source/androidassetpackagingtool.h deleted file mode 100644 index 252870f..0000000 --- a/source/androidassetpackagingtool.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef ANDROIDASSETPACKAGINGTOOL_H_ -#define ANDROIDASSETPACKAGINGTOOL_H_ - -#include "tool.h" - -class AndroidResourceBundle; -class AndroidSdk; - -class AndroidAssetPackagingTool: public Tool -{ -private: - const AndroidSdk &sdk; - -public: - AndroidAssetPackagingTool(Builder &, const AndroidSdk &); - - Target *create_target(const std::vector &, const std::string &) override; - -private: - static ExternalTask::Arguments _run(const AndroidResourceBundle &, Msp::FS::Path &); -}; - -#endif diff --git a/source/androidcompiler.cpp b/source/androidcompiler.cpp deleted file mode 100644 index 2321f03..0000000 --- a/source/androidcompiler.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include -#include -#include -#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(tool).get_architecture(); - - CustomizedTool::do_prepare(tool); - if(tag=="CXX") - { - tool.build_info.libs.push_back("gnustl_static"); - - unsigned version = tool.extra_data; - string version_str = format("%d.%d.%d", version>>16, (version>>8)&0xFF, version&0xFF); - FS::Path libstdcxx_dir = ndk.get_root_dir()/"sources"/"cxx-stl"/"gnu-libstdc++"; - FS::Path libstdcxx_path; - while(1) - { - libstdcxx_path = libstdcxx_dir/version_str; - if(FS::exists(libstdcxx_path)) - break; - - string::size_type dot = version_str.rfind('.'); - if(dot==string::npos) - { - tool.problems.push_back("C++ standard library not found"); - return; - } - - version_str = version_str.substr(0, dot); - } - - builder.get_logger().log("tools", "Found GNU libstdc++ in %s", libstdcxx_path); - - FS::Path public_dir = libstdcxx_path/"include"; - tool.system_path.push_back(public_dir); - tool.build_info.incpath.push_back(public_dir); - - FS::Path arch_path = libstdcxx_path/"libs"; - builder.get_logger().log("files", "Traversing %s", arch_path.str()); - string arch_dir = arch.best_match(list_files(arch_path)); - if(!arch_dir.empty()) - { - tool.build_info.incpath.push_back(libstdcxx_path/"libs"/arch_dir/"include"); - tool.build_info.libpath.push_back(libstdcxx_path/"libs"/arch_dir); - } - } -} diff --git a/source/androidcompiler.h b/source/androidcompiler.h deleted file mode 100644 index 86dfa27..0000000 --- a/source/androidcompiler.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef ANDROIDCOMPILER_H_ -#define ANDROIDCOMPILER_H_ - -#include "customizedtool.h" - -class AndroidNdk; - -class AndroidCompiler: public CustomizedTool -{ -private: - const AndroidNdk &ndk; - -public: - AndroidCompiler(Builder &, const Architecture &, const std::string &, const AndroidNdk &); - -protected: - void do_prepare(ToolData &) const override; -}; - -#endif diff --git a/source/androidlinker.cpp b/source/androidlinker.cpp deleted file mode 100644 index ec90249..0000000 --- a/source/androidlinker.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "androidlinker.h" -#include "androidtools.h" - -using namespace std; - -AndroidLinker::AndroidLinker(Builder &b, const Architecture &a, const AndroidNdk &ndk): - CustomizedTool(b, "LINK", a) -{ - build_info.sysroot = ndk.get_platform_sysroot(); - - set_command("gcc", true); - set_command((ndk.get_bin_dir()/command).str()); -} - -Target *AndroidLinker::create_target(const vector &sources, const string &) -{ - return CustomizedTool::create_target(sources, "shared"); -} diff --git a/source/androidlinker.h b/source/androidlinker.h deleted file mode 100644 index 7418d3c..0000000 --- a/source/androidlinker.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef ANDROIDLINKER_H_ -#define ANDROIDLINKER_H_ - -#include "customizedtool.h" - -class AndroidNdk; - -class AndroidLinker: public CustomizedTool -{ -public: - AndroidLinker(Builder &, const Architecture &, const AndroidNdk &); - - Target *create_target(const std::vector &, const std::string &) override; -}; - -#endif diff --git a/source/androidmanifestfile.cpp b/source/androidmanifestfile.cpp deleted file mode 100644 index 2dd1c96..0000000 --- a/source/androidmanifestfile.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include "androidapplicationcomponent.h" -#include "androidmanifestfile.h" -#include "builder.h" -#include "sourcepackage.h" - -using namespace std; -using namespace Msp; - -AndroidManifestFile::AndroidManifestFile(Builder &b, const AndroidApplicationComponent &a): - FileTarget(b, a.get_package(), a.get_package().get_temp_directory()/a.get_name()/"AndroidManifest.xml") -{ - component = &a; - tool = &builder.get_toolchain().get_tool("AMG"); - - add_dependency(package->get_build_file()); -} - -void AndroidManifestFile::set_native_library(SharedLibrary *lib) -{ - native_lib = lib; -} - -void AndroidManifestFile::set_orientation(const string &ori) -{ - orientation = ori; -} - -void AndroidManifestFile::add_permission(const string &perm) -{ - if(!any_equals(permissions, perm)) - permissions.push_back(perm); -} diff --git a/source/androidmanifestfile.h b/source/androidmanifestfile.h deleted file mode 100644 index 9c0ed21..0000000 --- a/source/androidmanifestfile.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef ANDROIDMANIFESTFILE_H_ -#define ANDROIDMANIFESTFILE_H_ - -#include -#include "filetarget.h" - -class AndroidApplicationComponent; -class SharedLibrary; - -/** -Metadata file for an Android application. -*/ -class AndroidManifestFile: public FileTarget -{ -private: - SharedLibrary *native_lib = 0; - std::vector 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 &get_permissions() const { return permissions; } - const std::string &get_orientation() const { return orientation; } -}; - -#endif diff --git a/source/androidmanifestgenerator.cpp b/source/androidmanifestgenerator.cpp deleted file mode 100644 index 0c48982..0000000 --- a/source/androidmanifestgenerator.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#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 &, 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("\n"); - IO::print(out, "\n", comp.get_name()); - out.write("\t\n"); - // TODO Make the icon name configurable - bool debuggable = binfo.debug; - IO::print(out, "\t\n", pkg.get_label(), debuggable); - if(SharedLibrary *native_lib = manifest.get_native_library()) - { - out.write("\t\t\n"); - IO::print(out, "\t\t\t\n", native_lib->get_libname()); - out.write("\t\t\t\n"); - out.write("\t\t\t\t\n"); - out.write("\t\t\t\t\n"); - out.write("\t\t\t\n"); - out.write("\t\t\n"); - } - out.write("\t\n"); - for(const string &p: manifest.get_permissions()) - IO::print(out, "\t\n", p); - out.write("\n"); - - return true; -} diff --git a/source/androidmanifestgenerator.h b/source/androidmanifestgenerator.h deleted file mode 100644 index 58fc0b2..0000000 --- a/source/androidmanifestgenerator.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef ANDROIDMANIFESTGENERATOR_H_ -#define ANDROIDMANIFESTGENERATOR_H_ - -#include "tool.h" - -class AndroidManifestFile; - -class AndroidManifestGenerator: public Tool -{ -public: - AndroidManifestGenerator(Builder &); - - Target *create_target(const std::vector &, const std::string &) override; - -private: - static bool _run(const AndroidManifestFile &); -}; - -#endif diff --git a/source/androidpackagefile.cpp b/source/androidpackagefile.cpp deleted file mode 100644 index 9dee78f..0000000 --- a/source/androidpackagefile.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "androidpackagefile.h" -#include "androidresourcebundle.h" -#include "component.h" -#include "sourcepackage.h" - -using namespace std; - -AndroidPackageFile::AndroidPackageFile(Builder &b, const Component &c, AndroidResourceBundle &resource_bundle, const vector &other_files): - FileTarget(b, c.get_package(), c.get_package().get_output_directory()/(c.get_name()+".apk")) -{ - component = &c; - - add_dependency(resource_bundle); - for(FileTarget *f: other_files) - add_dependency(*f); -} diff --git a/source/androidpackagefile.h b/source/androidpackagefile.h deleted file mode 100644 index c5f237a..0000000 --- a/source/androidpackagefile.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef ANDROIDPACKAGEFILE_H_ -#define ANDROIDPACKAGEFILE_H_ - -#include "filetarget.h" - -class AndroidResourceBundle; - -class AndroidPackageFile: public FileTarget -{ -public: - AndroidPackageFile(Builder &, const Component &, AndroidResourceBundle &, const std::vector &); - - const char *get_type() const override { return "AndroidPackageFile"; } -}; - -#endif diff --git a/source/androidresourcebundle.cpp b/source/androidresourcebundle.cpp deleted file mode 100644 index 8113c00..0000000 --- a/source/androidresourcebundle.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "androidmanifestfile.h" -#include "androidresourcebundle.h" -#include "component.h" -#include "sourcepackage.h" - -using namespace std; - -AndroidResourceBundle::AndroidResourceBundle(Builder &b, const Component &c, AndroidManifestFile &manifest, const vector &resources): - FileTarget(b, c.get_package(), c.get_package().get_temp_directory()/c.get_name()/(c.get_name()+".ap_")) -{ - component = &c; - - add_dependency(manifest); - for(FileTarget *f: resources) - add_dependency(*f); -} diff --git a/source/androidresourcebundle.h b/source/androidresourcebundle.h deleted file mode 100644 index 18c07e0..0000000 --- a/source/androidresourcebundle.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef ANDROIDRESOURCEBUNDLE_H_ -#define ANDROIDRESOURCEBUNDLE_H_ - -#include "filetarget.h" - -class AndroidManifestFile; - -class AndroidResourceBundle: public FileTarget -{ -public: - AndroidResourceBundle(Builder &, const Component &, AndroidManifestFile &, const std::vector &); - - const char *get_type() const override { return "AndroidResourceBundle"; } -}; - -#endif diff --git a/source/androidresourcefile.cpp b/source/androidresourcefile.cpp deleted file mode 100644 index d88f786..0000000 --- a/source/androidresourcefile.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include "androidresourcefile.h" -#include "component.h" - -using namespace std; -using namespace Msp; - -AndroidResourceFile::AndroidResourceFile(Builder &b, const Component &c, const FS::Path &p): - FileTarget(b, c.get_package(), p) -{ - string ext = FS::extpart(FS::basename(p)); - if(ext==".png" || ext==".jpg") - resource_type = "drawable"; - // TODO recognize various xml files; must inspect contents - else - resource_type = "raw"; - - install_location = "res/"+resource_type; -} diff --git a/source/androidresourcefile.h b/source/androidresourcefile.h deleted file mode 100644 index 162026c..0000000 --- a/source/androidresourcefile.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef ANDROIDRESOURCEFILE_H_ -#define ANDROIDRESOURCEFILE_H_ - -#include "filetarget.h" - -/** -A file destined to be used as a resource in an Android application. -*/ -class AndroidResourceFile: public FileTarget -{ -private: - std::string resource_type; - -public: - AndroidResourceFile(Builder &, const Component &, const Msp::FS::Path &); - - const char *get_type() const override { return "AndroidResourceFile"; } -}; - -#endif diff --git a/source/androidtools.cpp b/source/androidtools.cpp deleted file mode 100644 index e086dd0..0000000 --- a/source/androidtools.cpp +++ /dev/null @@ -1,258 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#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 version_parts = split(version_str, '.'); - unsigned version = lexical_cast(version_parts[0])<<16; - if(version_parts.size()>1) - version += lexical_cast(version_parts[1])<<8; - if(version_parts.size()>2) - version += lexical_cast(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(p.substr(8)); - if(api>63) - builder.get_logger().log("problems", "API level %d is too high", api); - else - supported_api_levels |= static_cast(1)<>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; (jlatest_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 platform_archs = list_filtered(platform_archs_dir, "^arch-"); - for(string &a: platform_archs) - a.erase(0, 5); - string use_arch = architecture.best_match(platform_archs); - - if(use_arch.empty()) - { - builder.get_logger().log("problems", "No matching Android NDK platform found"); - return; - } - - platform_sysroot = platform_archs_dir/("arch-"+use_arch); - builder.get_logger().log("tools", "Android NDK platform sysroot is %s", platform_sysroot); -} - - -AndroidTools::AndroidTools(Builder &builder, const Architecture &arch): - Toolchain("android-gnu", get_priority(arch)), - sdk(builder), - ndk(builder, arch, sdk) -{ - if(uint64_t common_api_levels = sdk.get_supported_api_levels()&ndk.get_supported_api_levels()) - { - unsigned api = 0; - for(unsigned i=32; i>0; i>>=1) - api += i*!!(common_api_levels>>(api+i)); - builder.get_logger().log("tools", "Using Android API level %d", api); - sdk.select_api_level(api); - ndk.select_api_level(api); - } - else - builder.get_logger().log("problems", "No usable Android platforms found"); - - add_tool(new AndroidCompiler(builder, arch, "CC", ndk)); - add_tool(new AndroidCompiler(builder, arch, "CXX", ndk)); - add_tool(new AndroidLinker(builder, arch, ndk)); - add_tool(new AndroidArchiver(builder, arch, ndk)); - add_tool(new AndroidManifestGenerator(builder)); - add_tool(new AndroidAssetPackagingTool(builder, sdk)); - add_tool(new ApkBuilder(builder)); - add_tool(new JarSigner(builder)); -} - -int AndroidTools::get_priority(const Architecture &arch) -{ - if(arch.get_system()=="android") - return 30; - else - return -10; -} diff --git a/source/androidtools.h b/source/androidtools.h deleted file mode 100644 index a88cdd3..0000000 --- a/source/androidtools.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef ANDROIDTOOLS_H_ -#define ANDROIDTOOLS_H_ - -#include -#include -#include "toolchain.h" - -class Architecture; -class Builder; - -class AndroidDevKit -{ -protected: - Builder &builder; - Msp::FS::Path root; - /* Needs refactoring if API levels go over 63. At present rate this will - take decades to occur. */ - uint64_t supported_api_levels = 0; - - AndroidDevKit(Builder &, const std::string &, const Msp::FS::Path & = Msp::FS::Path()); - ~AndroidDevKit() { } - -public: - const Msp::FS::Path &get_root_dir() const { return root; } - uint64_t get_supported_api_levels() const { return supported_api_levels; } - void select_api_level(unsigned); -protected: - virtual void init_api_level(unsigned) = 0; -}; - -class AndroidSdk: public AndroidDevKit -{ -private: - Msp::FS::Path build_tools_dir; - // TODO use a FileTarget for the jar - Msp::FS::Path platform_jar; - -public: - AndroidSdk(Builder &); - -private: - void find_build_tools_dir(); - void init_api_level(unsigned) override; - -public: - const Msp::FS::Path &get_build_tools_dir() const { return build_tools_dir; } - const Msp::FS::Path &get_platform_jar() const { return platform_jar; } -}; - -class AndroidNdk: public AndroidDevKit -{ -private: - const Architecture &architecture; - Msp::FS::Path bin_dir; - Msp::FS::Path common_sysroot; - Msp::FS::Path platform_sysroot; - -public: - AndroidNdk(Builder &, const Architecture &, const AndroidSdk &); -private: - static Msp::FS::Path create_default_path(const AndroidSdk &); - - void find_toolchain_dir(); - void init_api_level(unsigned) override; - -public: - const Msp::FS::Path &get_bin_dir() const { return bin_dir; } - const Msp::FS::Path &get_common_sysroot() const { return common_sysroot; } - const Msp::FS::Path &get_platform_sysroot() const { return platform_sysroot; } -}; - - -class AndroidTools: public Toolchain -{ -private: - AndroidSdk sdk; - AndroidNdk ndk; - -public: - AndroidTools(Builder &, const Architecture &); - - static int get_priority(const Architecture &); -}; - -#endif diff --git a/source/apkbuilder.cpp b/source/apkbuilder.cpp deleted file mode 100644 index f62e485..0000000 --- a/source/apkbuilder.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include -#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 &sources, const string &) -{ - AndroidResourceBundle *resource_bundle = 0; - vector other_files; - other_files.reserve(sources.size()); - for(Target *s: sources) - { - if(AndroidResourceBundle *r = dynamic_cast(s)) - resource_bundle = r; - else if(FileTarget *f = dynamic_cast(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(tgt); - const ApkBuilder &tool = dynamic_cast(*apk.get_tool()); - - ExternalTask::Arguments argv; - argv.push_back(tool.get_executable()->get_path().str()); - argv.push_back("u"); - - FS::Path input_path; - vector files; - files.reserve(apk.get_dependencies().size()); - for(Target *d: apk.get_dependencies()) - { - FileTarget *file = dynamic_cast(d); - Target *real = d->get_real_target(); - - if(dynamic_cast(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()->run(apk)); - return chain; -} diff --git a/source/apkbuilder.h b/source/apkbuilder.h deleted file mode 100644 index 8eaaeef..0000000 --- a/source/apkbuilder.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef APKBUILDER_H_ -#define APKBUILDER_H_ - -#include "tool.h" - -class AndroidPackageFile; - -class ApkBuilder: public Tool -{ -public: - ApkBuilder(Builder &); - - Target *create_target(const std::vector &, const std::string &) override; -protected: - void do_prepare(ToolData &) const override; - -private: - static Task *_run(const Target &); -}; - -#endif diff --git a/source/architecture.cpp b/source/architecture.cpp deleted file mode 100644 index 54ffcf0..0000000 --- a/source/architecture.cpp +++ /dev/null @@ -1,337 +0,0 @@ -#include -#include -#include -#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::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("%.dll"); - if(toolchain=="msvc") - { - add_pattern("%.obj"); - add_pattern("%.lib"); - add_pattern("%_static.lib"); - } - else - { - add_pattern("%.o"); - add_pattern("lib%.dll"); - add_pattern("lib%.dll.a"); - add_pattern("lib%.a"); - } - add_pattern("%.exe"); - } - else - { - add_pattern("%.o"); - if(system=="darwin") - add_pattern("lib%.dylib"); - else - add_pattern("lib%.so"); - add_pattern("lib%.a"); - add_pattern("%"); - } -} - -bool Architecture::match_name(const string &pattern, unsigned *quality) const -{ - bool negate = (pattern[0]=='!'); - vector 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 &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 -void Architecture::add_pattern(const string &pat) -{ - filename_patterns[typeid(T).name()].push_back(Pattern(pat)); -} - -void Architecture::resolve_aliases(vector &parts) -{ - for(unsigned i=0; i 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 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(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(a) -{ - add("prefix", &Loader::cross_prefix); -} - -void Architecture::Loader::cross_prefix(const string &p) -{ - if(!obj.native) - obj.cross_prefix = p; -} diff --git a/source/architecture.h b/source/architecture.h deleted file mode 100644 index 2cf4fa2..0000000 --- a/source/architecture.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef ARCHITECTURE_H_ -#define ARCHITECTURE_H_ - -#include -#include -#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 - { - 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> 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 &) 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 - const std::vector &get_patterns() const; - - template - std::string create_filename(const std::string &) const; - -private: - template - void add_pattern(const std::string &); - -private: - static void resolve_aliases(std::vector &); - void parse_specification(const std::string &); -}; - -template -inline const std::vector &Architecture::get_patterns() const -{ - auto i = filename_patterns.find(typeid(T).name()); - if(i!=filename_patterns.end()) - return i->second; - - static std::vector empty; - return empty; -} - -template -inline std::string Architecture::create_filename(const std::string &base) const -{ - const std::vector &patterns = get_patterns(); - return patterns.empty() ? base : patterns.front().apply(base); -} - -#endif diff --git a/source/binary.cpp b/source/binary.cpp deleted file mode 100644 index f245037..0000000 --- a/source/binary.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include -#include -#include -#include -#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 &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 static_libs; - vector shared_libs; - vector 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 &static_libs, vector &shared_libs, vector &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(real)) - { - /* Keep only the last occurrence of each static library. This - ensures the order is correct for linking. */ - auto i = find(static_libs, stlib); - if(i!=static_libs.end()) - static_libs.erase(i); - static_libs.push_back(stlib); - find_dependencies(stlib, static_libs, shared_libs, missing_libs); - } - else if(!any_equals(shared_libs, lib)) - shared_libs.push_back(lib); - } - else if(!any_equals(missing_libs, l)) - missing_libs.push_back(l); - } -} diff --git a/source/binary.h b/source/binary.h deleted file mode 100644 index ac5a855..0000000 --- a/source/binary.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef BINARY_H_ -#define BINARY_H_ - -#include "buildinfo.h" -#include "filetarget.h" - -class Component; -class ObjectFile; - -/** -Produces a binary file, which may be either a standalone executable or a shared -library. -*/ -class Binary: public FileTarget -{ -private: - BuildInfo static_binfo; - -protected: - std::vector objects; - - Binary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { } - Binary(Builder &, const Component &, const std::string &, const std::vector &); - -public: - void collect_build_info(BuildInfo &) const override; - -protected: - void find_dependencies() override; -private: - void find_dependencies(Target *, std::vector &, std::vector &, std::vector &); -}; - -#endif diff --git a/source/binarycomponent.cpp b/source/binarycomponent.cpp deleted file mode 100644 index 3209ccc..0000000 --- a/source/binarycomponent.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include -#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 objs; - vector 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 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(*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(src)->is_installable()) - build_graph.add_installed_target(*src); - - for(Target *s: src->get_side_effects()) - if(dynamic_cast(s)->is_installable()) - build_graph.add_installed_target(*s); - } - } - } - - Tool &linker = toolchain.get_tool("LINK"); - - vector 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(c) -{ - add("use", &Loader::use); -} - -void BinaryComponent::Loader::use(const string &n) -{ - const BinaryComponent *comp = dynamic_cast(&obj.package.get_component(n)); - if(!comp || comp->type!=LIBRARY) - throw logic_error(n+" is not a library"); - - obj.uses.push_back(comp); -} diff --git a/source/binarycomponent.h b/source/binarycomponent.h deleted file mode 100644 index ef145da..0000000 --- a/source/binarycomponent.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef BINARYCOMPONENT_H_ -#define BINARYCOMPONENT_H_ - -#include "component.h" - -class BinaryComponent: public Component -{ -public: - class Loader: public Msp::DataFile::DerivedObjectLoader - { - public: - Loader(BinaryComponent &); - private: - void use(const std::string &); - }; - - enum Type - { - LIBRARY, - PROGRAM, - MODULE - }; - -private: - Type type; - std::vector uses; - -public: - BinaryComponent(SourcePackage &p, const std::string &n, Type t): Component(p, n), type(t) { } - - Type get_type() const { return type; } - - void create_build_info() override; - void update_exported_build_info(BuildInfo &) const override; - void create_targets() const override; -}; - -#endif diff --git a/source/binarypackage.cpp b/source/binarypackage.cpp deleted file mode 100644 index adfca1a..0000000 --- a/source/binarypackage.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include -#include -#include -#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 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(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(p) -{ - add("build_info", &Loader::build_info); - add("header", &Loader::header); -} - -void BinaryPackage::Loader::build_info() -{ - load_sub(obj.export_binfo); -} - -void BinaryPackage::Loader::header(const string &h) -{ - obj.headers.push_back(h); -} diff --git a/source/binarypackage.h b/source/binarypackage.h deleted file mode 100644 index 62a8acf..0000000 --- a/source/binarypackage.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef BINARYPACKAGE_H_ -#define BINARYPACKAGE_H_ - -#include "package.h" - -/** -Represents a package that is installed on the system, but can't be built by -Builder. -*/ -class BinaryPackage: public Package -{ -public: - class Loader: public Msp::DataFile::DerivedObjectLoader - { - public: - Loader(BinaryPackage &); - private: - void build_info(); - void header(const std::string &); - }; - - using Flags = std::vector; - -private: - Msp::FS::Path base_path; - std::vector headers; - BuildInfo static_binfo; - -public: - BinaryPackage(Builder &, const std::string &); - - const BuildInfo &get_static_build_info() const { return static_binfo; } - - static BinaryPackage *from_flags(Builder &, const std::string &, const Flags &, const Flags & = Flags()); -private: - static void process_flags(const Flags &, BuildInfo &); - void do_prepare() override; -}; - -#endif diff --git a/source/booleanevaluator.cpp b/source/booleanevaluator.cpp deleted file mode 100644 index b7c399f..0000000 --- a/source/booleanevaluator.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include -#include -#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) -#include -#include - -class BooleanEvaluator -{ -public: - using ValueFunction = std::function; - using CompareFunction = std::function; - -private: - CompareFunction func; - std::string ops; - std::vector var_stack; - std::vector value_stack; - std::vector op_stack; - bool last_was_op; - -public: - BooleanEvaluator(const ValueFunction &); - BooleanEvaluator(const CompareFunction &); - - bool evaluate(const std::string &); -private: - void push_op(char); - bool pop_value(); - void collapse(unsigned); - unsigned precedence(char); - bool is_unary(char); - bool is_logic(char); -}; - -#endif diff --git a/source/builder.cpp b/source/builder.cpp deleted file mode 100644 index 3fc60be..0000000 --- a/source/builder.cpp +++ /dev/null @@ -1,364 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 Builder::get_build_types() const -{ - vector 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 Builder::collect_problems() const -{ - vector problems; - set broken_packages; - set broken_components; - set 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 tasks; - - unsigned count = 0; - - bool fail = false; - bool finish = false; - bool starved = false; - - while(!finish) - { - if(tasks.size()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; iwait(); - 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 clean_tgts; - deque 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(b), - options(o), - conf_all(a) -{ - add("architecture", &Loader::architecture); - add("binary_package", &Loader::binpkg); - add("build_type", &Loader::build_type); - add("package", &Loader::package); - - if(!obj.top_loader) - obj.top_loader = this; - else if(!options && obj.top_loader!=this && obj.top_loader->conf_all) - options = obj.top_loader->options; -} - -Builder::Loader::~Loader() -{ - if(obj.top_loader==this) - obj.top_loader = 0; -} - -void Builder::Loader::architecture(const string &n) -{ - if(obj.current_arch->match_name(n)) - load_sub(*obj.current_arch); -} - -void Builder::Loader::binpkg(const string &n) -{ - BinaryPackage *pkg = new BinaryPackage(obj, n); - load_sub(*pkg); -} - -void Builder::Loader::build_type(const string &n) -{ - BuildType btype(n); - load_sub(btype); - auto i = obj.build_types.insert({ n, btype }).first; - if(!obj.build_type) - obj.build_type = &i->second; -} - -void Builder::Loader::package(const string &n) -{ - SourcePackage *pkg = new SourcePackage(obj, n, get_source()); - - load_sub(*pkg, options); - - if(obj.build_type) - pkg->set_build_type(*obj.build_type); -} diff --git a/source/builder.h b/source/builder.h deleted file mode 100644 index e06cec3..0000000 --- a/source/builder.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef BUILDER_H_ -#define BUILDER_H_ - -#include -#include -#include -#include -#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 - { - 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 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 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 collect_problems() const; - - /** Loads a build file. If opts is not null, it is used to configure any - packages loaded from this file. If all is true, external packages are also - configured. */ - void load_build_file(const Msp::FS::Path &, const Config::InputOptions *opts = 0, bool all = false); - - /** Saves package configuration and dependency caches. */ - void save_caches(); - - /** Builds the goal targets. The build graph must be prepared first. */ - int build(unsigned jobs = 1, bool dry_run = false, bool show_progress = false); - - /** Cleans buildable targets. If all is true, cleans all packages. - Otherwise cleans only the default package. */ - int clean(bool all = false, bool dry_run = false); - - int do_create_makefile(); -}; - -#endif diff --git a/source/buildercli.cpp b/source/buildercli.cpp deleted file mode 100644 index bc18eed..0000000 --- a/source/buildercli.cpp +++ /dev/null @@ -1,374 +0,0 @@ -#include -#include -#include -#include -#include -#include -#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("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 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 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 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(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 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(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(package_manager.get_main_package()); - const Config &config = main_pkg.get_config(); - const auto &options = config.get_options(); - const Package::Requirements &requires = main_pkg.get_required_packages(); - - if(!requires.empty() || !options.empty()) - IO::print("\nHelp for package %s:\n", main_pkg.get_name()); - - if(!requires.empty()) - { - IO::print("\nRequired packages:\n "); - for(auto i=requires.begin(); i!=requires.end(); ++i) - { - if(i!=requires.begin()) - IO::print(", "); - IO::print((*i)->get_name()); - } - IO::print("\n"); - } - - if(!options.empty()) - { - IO::print("\nPackage configuration:\n"); - for(const auto &kvp: options) - { - const Config::Option &opt = kvp.second; - string line = format(" %s: %s (%s)", opt.name, opt.description, opt.value); - if(!opt.choices.empty()) - { - line += " {"; - line += join(opt.choices.begin(), opt.choices.end(), " "); - line += '}'; - } - else if(opt.value!=opt.default_value) - line += format(" [%s]", opt.default_value); - IO::print("%s\n", line); - } - } -} diff --git a/source/buildercli.h b/source/buildercli.h deleted file mode 100644 index 3b15541..0000000 --- a/source/buildercli.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef BUILDERCLI_H_ -#define BUILDERCLI_H_ - -#include -#include "builder.h" - -class Analyzer; - -/** -Provides a command-line interface for Builder. -*/ -class BuilderCLI: public Msp::RegisteredApplication -{ -private: - std::vector 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 what_if; - bool conf_all = false; - bool conf_only = false; - bool build_all = false; - bool create_makefile = false; - -public: - BuilderCLI(int, char **); - ~BuilderCLI(); - - int main() override; - -private: - bool prepare_build(); - Target *resolve_target(const std::string &); - - void package_help(); -}; - -#endif diff --git a/source/buildgraph.cpp b/source/buildgraph.cpp deleted file mode 100644 index 070f934..0000000 --- a/source/buildgraph.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "builder.h" -#include "buildgraph.h" -#include "component.h" -#include "sourcepackage.h" -#include "tool.h" -#include "virtualtarget.h" - -using namespace std; - -BuildGraph::BuildGraph(Builder &b): - builder(b), - goals(new VirtualTarget(builder, "goals")) -{ - Target *world = new VirtualTarget(builder, "world"); - world->add_dependency(*new VirtualTarget(builder, "default")); - world->add_dependency(*new VirtualTarget(builder, "install")); - world->add_dependency(*new VirtualTarget(builder, "archives")); -} - -BuildGraph::~BuildGraph() -{ - for(const auto &kvp: targets) - delete kvp.second; -} - -Target *BuildGraph::get_target(const string &n) const -{ - auto i = targets.find(n); - if(i!=targets.end()) - return i->second; - return 0; -} - -void BuildGraph::add_target(Target *t) -{ - targets.insert({ t->get_name(), t }); -} - -void BuildGraph::add_primary_target(Target &t) -{ - get_target("world")->add_dependency(t); - - Package *main_pkg = &builder.get_package_manager().get_main_package(); - if(t.get_package()==main_pkg && t.get_component() && t.get_component()->is_default()) - get_target("default")->add_dependency(t); -} - -void BuildGraph::add_installed_target(Target &t) -{ - Target *inst_tgt = 0; - if(const Tool *tool = t.get_tool()) - inst_tgt = tool->create_install(t); - if(!inst_tgt) - inst_tgt = builder.get_toolchain().get_tool("CP").create_target(t); - get_target("install")->add_dependency(*inst_tgt); -} - -void BuildGraph::add_goal(Target &t) -{ - goals->add_dependency(t); -} - -void BuildGraph::prepare() -{ - if(goals->get_dependencies().empty()) - add_goal(*get_target("default")); - goals->prepare(); -} - -void BuildGraph::force_full_rebuild() -{ - for(const auto &kvp: targets) - if(kvp.second->is_buildable() && !kvp.second->needs_rebuild()) - kvp.second->force_rebuild(); -} - -unsigned BuildGraph::count_rebuild_targets() const -{ - unsigned count = 0; - for(const auto &kvp: targets) - if(kvp.second->is_buildable() && kvp.second->needs_rebuild()) - ++count; - return count; -} - -Target *BuildGraph::get_buildable_target() const -{ - return goals->get_buildable_target(); -} diff --git a/source/buildgraph.h b/source/buildgraph.h deleted file mode 100644 index 371b544..0000000 --- a/source/buildgraph.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef BUILDGRAPH_H_ -#define BUILDGRAPH_H_ - -#include -#include - -class Builder; -class Target; - -/** -Manages a graph of targets. -*/ -class BuildGraph -{ -private: - Builder &builder; - std::map 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 &get_targets() const { return targets; } - - /** Adds a target. It can later be retrieved by name. Called from Target - constructor. */ - void add_target(Target *); - - /** Adds a target that is a primary build goal. Such targets will be added - as dependencies of the "world" virtual target. If the target belongs to a - default component of the main package, it's also added to the "default" - virtual target. */ - void add_primary_target(Target &); - - /** Adds a target that will be installed. A new InstalledFile target is - created and added as a dependency to the "install" virtual target. */ - void add_installed_target(Target &); - - /** Adds a target as a toplevel goal. These are stored as dependencies of - the "goals" virtual target. */ - void add_goal(Target &); - - Target &get_goals() const { return *goals; } - - /** Prepares all toplevel goals for building. If no goals are defined, the - "default" target is added as a goal. */ - void prepare(); - - /** Marks all buildable targets to be rebuilt. The graph must be prepared - first. */ - void force_full_rebuild(); - - /** Returns the number of targets that are going to be rebuilt. The graph - must be prepared first. */ - unsigned count_rebuild_targets() const; - - /** Returns a target that can be built and is needed for building the goal - targets. Null */ - Target *get_buildable_target() const; -}; - -#endif diff --git a/source/buildinfo.cpp b/source/buildinfo.cpp deleted file mode 100644 index 8e11670..0000000 --- a/source/buildinfo.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include -#include -#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 -void unique(vector &v) -{ - vector 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(c)); }); - string::size_type num = i-std.begin(); - type = std.substr(0, num); - year = lexical_cast(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(bi) -{ - add("debug", &BuildInfo::debug); - add("define", &Loader::define); - add("incpath", &Loader::incpath); - add("keep_symbol", &Loader::keep_symbol); - add("libpath", &Loader::libpath); - add("library", &Loader::library); - add("libmode", &BuildInfo::libmode); - add("libmode", &Loader::libmode_for_lib); - add("local_incpath", &Loader::local_incpath); - add("optimize", &BuildInfo::optimize); - add("runtime_path_mode", &BuildInfo::rpath_mode); - add("standard", &Loader::standard); - add("strip", &BuildInfo::strip); - add("sysroot", &Loader::sysroot); - add("threads", &BuildInfo::threads); - add("warning_level", &BuildInfo::warning_level); - add("fatal_warnings", &BuildInfo::fatal_warnings); -} - -void BuildInfo::Loader::incpath(const string &s) -{ - obj.incpath.push_back(s); -} - -void BuildInfo::Loader::define(const string &d, const string &v) -{ - obj.defines[d] = v; -} - -void BuildInfo::Loader::keep_symbol(const string &s) -{ - obj.keep_symbols.push_back(s); -} - -void BuildInfo::Loader::libmode_for_lib(const string &l, LibraryMode m) -{ - obj.libmodes[l] = m; -} - -void BuildInfo::Loader::libpath(const string &s) -{ - obj.libpath.push_back(s); -} - -void BuildInfo::Loader::library(const string &s) -{ - obj.libs.push_back(s); -} - -void BuildInfo::Loader::local_incpath(const string &s) -{ - obj.local_incpath.push_back(s); -} - -void BuildInfo::Loader::standard(DataFile::Symbol tag, const string &std) -{ - obj.standards[tag.name] = std; -} - -void BuildInfo::Loader::sysroot(const string &s) -{ - obj.sysroot = s; -} - - -void operator>>(const LexicalConverter &conv, BuildInfo::LibraryMode &libmode) -{ - if(conv.get()=="FORCE_STATIC") - libmode = BuildInfo::FORCE_STATIC; - else if(conv.get()=="STATIC") - libmode = BuildInfo::STATIC; - else if(conv.get()=="DYNAMIC") - libmode = BuildInfo::DYNAMIC; - else if(conv.get()=="FORCE_DYNAMIC") - libmode = BuildInfo::FORCE_DYNAMIC; - else - throw lexical_error(format("Conversion of '%s' to LibraryMode", conv.get())); -} - - -void operator>>(const LexicalConverter &conv, BuildInfo::RuntimePathMode &rpath_mode) -{ - if(conv.get()=="NONE") - rpath_mode = BuildInfo::NO_RPATH; - else if(conv.get()=="RELATIVE") - rpath_mode = BuildInfo::RELATIVE; - else if(conv.get()=="ABSOLUTE") - rpath_mode = BuildInfo::ABSOLUTE; - else - throw lexical_error(format("Conversion of '%s' to RuntimePathMode", conv.get())); -} diff --git a/source/buildinfo.h b/source/buildinfo.h deleted file mode 100644 index 26afabf..0000000 --- a/source/buildinfo.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef BUILDINFO_H_ -#define BUILDINFO_H_ - -#include -#include -#include -#include - -/** -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 - { - 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 - 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 sysroot; - std::map defines; - std::vector incpath; - std::vector local_incpath; - std::vector libpath; - std::vector libs; - Tracked libmode = DYNAMIC; - Tracked rpath_mode = NO_RPATH; - std::map libmodes; - std::vector keep_symbols; - std::map standards; - Tracked threads = false; - Tracked debug = false; - Tracked optimize = 0; - Tracked strip = false; - Tracked warning_level = 0; - Tracked fatal_warnings = false; - - /** Returns the library mode for linking a particular library. If no mode - has been specified for that library, the the global library mode is - returned. */ - LibraryMode get_libmode_for(const std::string &) const; - - /** Updates the BuildInfo from another one. Lists are concatenated, with - the first occurrence of each item preserved. Scalars are overwritten. - - The update level determines what information is updated. */ - void update_from(const BuildInfo &, UpdateLevel = LOCAL); -}; - -#endif diff --git a/source/buildtype.cpp b/source/buildtype.cpp deleted file mode 100644 index 2485ca5..0000000 --- a/source/buildtype.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "buildtype.h" - -using namespace std; -using namespace Msp; - -BuildType::Loader::Loader(BuildType &b): - DataFile::ObjectLoader(b) -{ - add("build_info", &Loader::build_info); -} - -void BuildType::Loader::build_info() -{ - load_sub(obj.build_info); -} diff --git a/source/buildtype.h b/source/buildtype.h deleted file mode 100644 index c16e8f9..0000000 --- a/source/buildtype.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef BUILDTYPE_H_ -#define BUILDTYPE_H_ - -#include -#include -#include "buildinfo.h" - -class BuildType -{ -public: - class Loader: public Msp::DataFile::ObjectLoader - { - public: - Loader(BuildType &); - - private: - void build_info(); - }; - -private: - std::string name; - BuildInfo build_info; - -public: - BuildType(const std::string &n): name(n) { } - - const std::string &get_name() const { return name; } - const BuildInfo &get_build_info() const { return build_info; } -}; - -#endif diff --git a/source/builtintools.cpp b/source/builtintools.cpp deleted file mode 100644 index c2c11e6..0000000 --- a/source/builtintools.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "builtintools.h" -#include "copy.h" -#include "compilecommandsgenerator.h" -#include "pkgconfiggenerator.h" -#include "tar.h" -#include "vcxprojectgenerator.h" -#include "vssolutiongenerator.h" - -BuiltinTools::BuiltinTools(Builder &builder) -{ - add_tool(new Copy(builder)); - add_tool(new Tar(builder)); - add_tool(new PkgConfigGenerator(builder)); - add_tool(new VcxProjectGenerator(builder)); - add_tool(new VsSolutionGenerator(builder)); - add_tool(new CompileCommandsGenerator(builder)); -} diff --git a/source/builtintools.h b/source/builtintools.h deleted file mode 100644 index e3b0256..0000000 --- a/source/builtintools.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef BUILTINTOOLS_H_ -#define BUILTINTOOLS_H_ - -#include "toolchain.h" - -class Builder; - -class BuiltinTools: public Toolchain -{ -public: - BuiltinTools(Builder &); -}; - -#endif diff --git a/source/cache.cpp b/source/cache.cpp deleted file mode 100644 index 6baacce..0000000 --- a/source/cache.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "builder.h" -#include "cache.h" -#include "sourcepackage.h" -#include "target.h" - -using namespace std; -using namespace Msp; - -namespace { - -unsigned read_count(IO::Base &in) -{ - unsigned char head = in.get(); - if((head&0xC0)==0xC0) - { - unsigned char tail = in.get(); - return (head&0x3F)<<8 | tail; - } - else - return head; -} - -string read_string(IO::Base &in) -{ - unsigned len = read_count(in); - char buf[0x4000]; - len = in.read(buf, len); - return string(buf, len); -} - -void write_count(IO::Base &out, unsigned count) -{ - if(count<0xC0) - out.put(count); - else if(count<0x4000) - { - out.put(0xC0 | (count>>8)); - out.put(count&0xFF); - } - else - throw invalid_argument("write_count"); -} - -void write_string(IO::Base &out, const string &str) -{ - write_count(out, str.size()); - out.write(str); -} - -} - - -Cache::Cache(SourcePackage &p): - package(p), - filename(package.get_temp_directory()/"../cache") -{ } - -void Cache::set_value(const Target *tgt, const string &k, const string &v) -{ - Values vl; - vl.push_back(v); - set_values(tgt, k, vl); -} - -void Cache::append_value(const Target *tgt, const string &k, const string &v) -{ - Key key(tgt->get_name(), k); - auto i = data.find(key); - if(i==data.end()) - i = data.insert({ key, Values() }).first; - i->second.push_back(v); - changed = true; - package.get_builder().get_logger().log("cache", "Updated key %s %s+ %s", tgt->get_name(), k, v); -} - -void Cache::set_values(const Target *tgt, const string &k, const Values &v) -{ - data[Key(tgt->get_name(), k)] = v; - changed = true; - package.get_builder().get_logger().log("cache", "Updated key %s %s: %s", tgt->get_name(), k, join(v.begin(), v.end())); -} - -const string &Cache::get_value(const Target *tgt, const string &k) -{ - const Values &values = get_values(tgt, k); - if(values.empty()) - throw logic_error("values.empty()"); - return values.front(); -} - -const Cache::Values &Cache::get_values(const Target *tgt, const string &k) -{ - return get_item(data, Key(tgt->get_name(), k)); -} - -bool Cache::has_key(const Target *tgt, const string &k) -{ - return data.count(Key(tgt->get_name(), k)); -} - -void Cache::load() -{ - if(FS::Stat st = FS::stat(filename)) - { - package.get_builder().get_logger().log("files", "Reading %s", filename); - IO::BufferedFile in(filename.str()); - - while(!in.eof()) - { - Key key; - key.first = read_string(in); - key.second = read_string(in); - if(key.first.empty() || key.second.empty()) - break; - Values &values = data[key]; - for(unsigned count = read_count(in); count; --count) - values.push_back(read_string(in)); - package.get_builder().get_logger().log("cache", "Loaded key %s %s: %s", key.first, key.second, join(values.begin(), values.end())); - } - - mtime = st.get_modify_time(); - } -} - -void Cache::save() const -{ - if(data.empty() || !changed) - return; - - FS::Path dir = FS::dirname(filename); - if(!FS::exists(dir)) - FS::mkpath(dir, 0755); - package.get_builder().get_logger().log("files", "Writing %s", filename); - IO::BufferedFile out(filename.str(), IO::M_WRITE); - - for(const auto &kvp: data) - { - write_string(out, kvp.first.first); - write_string(out, kvp.first.second); - write_count(out, kvp.second.size()); - for(const string &v: kvp.second) - write_string(out, v); - } - - changed = false; -} diff --git a/source/cache.h b/source/cache.h deleted file mode 100644 index 9193dac..0000000 --- a/source/cache.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef CACHE_H_ -#define CACHE_H_ - -#include -#include -#include -#include - -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; -private: - using Key = std::pair; - - SourcePackage &package; - Msp::FS::Path filename; - std::map data; - Msp::Time::TimeStamp mtime; - mutable bool changed = false; - -public: - Cache(SourcePackage &p); - - /// Sets a key to a single value, replacing any existing values. - void set_value(const Target *, const std::string &, const std::string &); - - /// Appends a value to a key. If the key does not exist, it is created. - void append_value(const Target *, const std::string &, const std::string &); - - /// Sets a key to a list of values, replacing any existing values. - void set_values(const Target *, const std::string &, const Values &); - - /** Returns the first value from a key. The key must exist and be - non-empty. */ - const std::string &get_value(const Target *, const std::string &); - - /// Returns the values from a key. The key must exist. - const Values &get_values(const Target *, const std::string &); - - /// Indicates whether a key exists. - bool has_key(const Target *, const std::string &); - - /// Returns the last modification timestamp of the cache. - const Msp::Time::TimeStamp &get_mtime() const { return mtime; } - - /** Loads the cache file and sets the last modification timestamp - accordingly. */ - void load(); - - /** Saves the cache. Does nothing if there is no data to save or nothing - has changed. */ - void save() const; -}; - -#endif diff --git a/source/chainedtask.cpp b/source/chainedtask.cpp deleted file mode 100644 index a232ef8..0000000 --- a/source/chainedtask.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#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(currentcheck())) ; - - return final_status; -} - -Task::Status ChainedTask::wait() -{ - while(currentwait())) ; - - return final_status; -} - -bool ChainedTask::process(Status sub_status) -{ - if(sub_status==SUCCESS && current+1start(); - return true; - } - - if(sub_status!=RUNNING) - { - // The task is not running anymore and either failed or was the last one - current = tasks.size(); - final_status = sub_status; - } - - return false; -} diff --git a/source/chainedtask.h b/source/chainedtask.h deleted file mode 100644 index 30a3b3e..0000000 --- a/source/chainedtask.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef CHAINEDTASK_H_ -#define CHAINEDTASK_H_ - -#include -#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 tasks; - unsigned current = 0; - Status final_status = RUNNING; - -public: - ChainedTask(Task *t) { add_task(t); } - ~ChainedTask(); - - void add_task(Task *); - - std::string get_command() const override; - void start() override; - Status check() override; - Status wait() override; -private: - bool process(Status); -}; - -#endif diff --git a/source/clangcompiler.cpp b/source/clangcompiler.cpp deleted file mode 100644 index bcd73d1..0000000 --- a/source/clangcompiler.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "clangcompiler.h" - -using namespace std; -using namespace Msp; - -ClangCompiler::ClangCompiler(Builder &b, const Architecture &a, const string &t): - CustomizedTool(b, t, a) -{ - set_command((tag=="CXX" ? "clang++" : "clang"), true); -} diff --git a/source/clangcompiler.h b/source/clangcompiler.h deleted file mode 100644 index af2c05d..0000000 --- a/source/clangcompiler.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CLANGCOMPILER_H_ -#define CLANGCOMPILER_H_ - -#include "customizedtool.h" - -class ClangCompiler: public CustomizedTool -{ -public: - ClangCompiler(Builder &, const Architecture &, const std::string &); -}; - -#endif diff --git a/source/clanglinker.cpp b/source/clanglinker.cpp deleted file mode 100644 index 5089028..0000000 --- a/source/clanglinker.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#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(tool).get_tag(), p); - tool.system_path.push_back(p); - break; - } -} diff --git a/source/clanglinker.h b/source/clanglinker.h deleted file mode 100644 index de65e64..0000000 --- a/source/clanglinker.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef CLANGLINKER_H_ -#define CLANGLINKER_H_ - -#include "customizedtool.h" - -class ClangLinker: public CustomizedTool -{ -public: - ClangLinker(Builder &, const Architecture &); - -protected: - void do_prepare(ToolData &) const override; -}; - -#endif diff --git a/source/clangtools.cpp b/source/clangtools.cpp deleted file mode 100644 index 8e8973d..0000000 --- a/source/clangtools.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "architecture.h" -#include "clangcompiler.h" -#include "clanglinker.h" -#include "clangtools.h" - -using namespace std; - -ClangTools::ClangTools(Builder &builder, const Architecture &arch): - Toolchain("clang", get_priority(arch)) -{ - add_tool(new ClangCompiler(builder, arch, "CC")); - add_tool(new ClangCompiler(builder, arch, "CXX")); - add_tool(new ClangCompiler(builder, arch, "OBJC")); - - add_tool(new ClangLinker(builder, arch)); -} - -int ClangTools::get_priority(const Architecture &arch) -{ - if(arch.get_toolchain()=="clang") - return 20; - else if(arch.get_system()=="darwin" || arch.get_system()=="freebsd") - return 10; - else - return 0; -} diff --git a/source/clangtools.h b/source/clangtools.h deleted file mode 100644 index 9faaf1a..0000000 --- a/source/clangtools.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef CLANGTOOLS_H_ -#define CLANGTOOLS_H_ - -#include "toolchain.h" - -class Architecture; -class Builder; - -class ClangTools: public Toolchain -{ -public: - ClangTools(Builder &, const Architecture &); - - static int get_priority(const Architecture &); -}; - -#endif diff --git a/source/cli/analyzer.cpp b/source/cli/analyzer.cpp new file mode 100644 index 0000000..e12e7fc --- /dev/null +++ b/source/cli/analyzer.cpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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(&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(&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 &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 col_width; + + // Determine column widths + for(const vector &r: table) + { + if(col_width.size() &r: table) + { + string line; + for(unsigned j=0; j0) + line += " "; + line += lexical_cast(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()get_name(); +} + +bool Analyzer::target_order_full(const Target *t1, const Target *t2) +{ + const FileTarget *ft1 = dynamic_cast(t1); + const FileTarget *ft2 = dynamic_cast(t2); + if(!ft1) + { + if(ft2) + return true; + return target_order(t1, t2); + } + else if(!ft2) + return false; + return ft1->get_path().str()get_path().str(); +} diff --git a/source/cli/analyzer.h b/source/cli/analyzer.h new file mode 100644 index 0000000..d30de04 --- /dev/null +++ b/source/cli/analyzer.h @@ -0,0 +1,58 @@ +#ifndef ANALYZER_H_ +#define ANALYZER_H_ + +#include +#include +#include +#include + +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; + + Builder &builder; + Mode mode = DEPS; + std::vector table; + unsigned max_depth = 0; + bool full_paths = false; + std::map > rdepends; + +public: + Analyzer(Builder &b): builder(b) { } + + void set_mode(Mode m) { mode = m; } + void set_max_depth(unsigned m) { max_depth = m; } + void set_full_paths(bool f) { full_paths = f; } + + /// Performs the analysis and prints out the resulting dependency tree. + void analyze(); + +private: + /** Adds rows for a target, then recursively adds rows for dependencies as + needed. */ + void build_depend_table(Target &, unsigned); + + /// Prints out the table that resulted from the analysis. + void print_table() const; + + static bool target_order(const Target *, const Target *); + static bool target_order_full(const Target *, const Target *); +}; + +#endif diff --git a/source/cli/buildercli.cpp b/source/cli/buildercli.cpp new file mode 100644 index 0000000..cb0897c --- /dev/null +++ b/source/cli/buildercli.cpp @@ -0,0 +1,374 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "analyzer.h" +#include "buildercli.h" + +using namespace std; +using namespace Msp; + +BuilderCLI::BuilderCLI(int argc, char **argv): + RegisteredApplication("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 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 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 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(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 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(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(package_manager.get_main_package()); + const Config &config = main_pkg.get_config(); + const auto &options = config.get_options(); + const Package::Requirements &requires = main_pkg.get_required_packages(); + + if(!requires.empty() || !options.empty()) + IO::print("\nHelp for package %s:\n", main_pkg.get_name()); + + if(!requires.empty()) + { + IO::print("\nRequired packages:\n "); + for(auto i=requires.begin(); i!=requires.end(); ++i) + { + if(i!=requires.begin()) + IO::print(", "); + IO::print((*i)->get_name()); + } + IO::print("\n"); + } + + if(!options.empty()) + { + IO::print("\nPackage configuration:\n"); + for(const auto &kvp: options) + { + const Config::Option &opt = kvp.second; + string line = format(" %s: %s (%s)", opt.name, opt.description, opt.value); + if(!opt.choices.empty()) + { + line += " {"; + line += join(opt.choices.begin(), opt.choices.end(), " "); + line += '}'; + } + else if(opt.value!=opt.default_value) + line += format(" [%s]", opt.default_value); + IO::print("%s\n", line); + } + } +} diff --git a/source/cli/buildercli.h b/source/cli/buildercli.h new file mode 100644 index 0000000..f4b2ad7 --- /dev/null +++ b/source/cli/buildercli.h @@ -0,0 +1,49 @@ +#ifndef BUILDERCLI_H_ +#define BUILDERCLI_H_ + +#include +#include + +class Analyzer; + +/** +Provides a command-line interface for Builder. +*/ +class BuilderCLI: public Msp::RegisteredApplication +{ +private: + std::vector 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 what_if; + bool conf_all = false; + bool conf_only = false; + bool build_all = false; + bool create_makefile = false; + +public: + BuilderCLI(int, char **); + ~BuilderCLI(); + + int main() override; + +private: + bool prepare_build(); + Target *resolve_target(const std::string &); + + void package_help(); +}; + +#endif diff --git a/source/compilecommandsgenerator.cpp b/source/compilecommandsgenerator.cpp deleted file mode 100644 index bbd4e98..0000000 --- a/source/compilecommandsgenerator.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include -#include -#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 &, 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(kvp.second)) - { - FS::Path src_path = obj->get_source().get_path(); - Task *task = kvp.second->build(); - if(!first) - out.put(','); - IO::print(out, "\n\t{\n"); - IO::print(out, "\t\t\"file\": \"%s\"\n", src_path); - IO::print(out, "\t\t\"command\": \"%s\"\n", c_escape(task->get_command())); - IO::print(out, "\t\t\"directory\": \"%s\"\n", work_dir); - IO::print(out, "\t}"); - delete task; - first = false; - } - - IO::print(out, "\n]\n"); - - return true; -} diff --git a/source/compilecommandsgenerator.h b/source/compilecommandsgenerator.h deleted file mode 100644 index a6fa6f9..0000000 --- a/source/compilecommandsgenerator.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef COMPILECOMMANDSGENERATOR_H_ -#define COMPILECOMMANDSGENERATOR_H_ - -#include "tool.h" - -class CompileCommandsJson; - -class CompileCommandsGenerator: public Tool -{ -public: - CompileCommandsGenerator(Builder &); - - Target *create_target(const std::vector &, const std::string &) override; - -private: - static bool _run(const CompileCommandsJson &); -}; - -#endif diff --git a/source/compilecommandsjson.cpp b/source/compilecommandsjson.cpp deleted file mode 100644 index ff24d7e..0000000 --- a/source/compilecommandsjson.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "builder.h" -#include "package.h" -#include "compilecommandsjson.h" -#include "objectfile.h" - -CompileCommandsJson::CompileCommandsJson(Builder &b, const SourcePackage &p): - FileTarget(b, p, p.get_source_directory()/("compile_commands.json")) -{ - tool = &builder.get_toolchain().get_tool("CCJG"); -} - -void CompileCommandsJson::find_dependencies() -{ - for(const auto &kvp: builder.get_build_graph().get_targets()) - if(kvp.second->is_buildable() && kvp.second->get_package()==package && dynamic_cast(kvp.second)) - kvp.second->prepare(); -} diff --git a/source/compilecommandsjson.h b/source/compilecommandsjson.h deleted file mode 100644 index 8798849..0000000 --- a/source/compilecommandsjson.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef COMPILECOMMANDSJSON_H_ -#define COMPILECOMMANDSJSON_H_ - -#include "sourcepackage.h" -#include "filetarget.h" - -class CompileCommandsJson: public FileTarget -{ -private: - -public: - CompileCommandsJson(Builder &, const SourcePackage &); - - const char *get_type() const override { return "CompileCommandsJson"; } - -protected: - void find_dependencies() override; -}; - -#endif diff --git a/source/component.cpp b/source/component.cpp deleted file mode 100644 index 4fddcef..0000000 --- a/source/component.cpp +++ /dev/null @@ -1,180 +0,0 @@ -#include -#include -#include -#include -#include -#include -#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 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 Component::collect_source_files() const -{ - vector files; - for(const FS::Path &p: sources) - { - if(FS::is_dir(p)) - { - vector 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 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(c), - ConditionalLoader(c.package, format("%s/%s", c.package.get_name(), c.name)) -{ - add("overlay", &Loader::overlay); - add("source", &Loader::source); - add("install", &Component::install); - add("install_map", &Loader::install_map); - add("build_info", &Loader::build_info); - add("require", &Loader::require); - add("default", &Component::deflt); -} - -void Component::Loader::build_info() -{ - load_sub(obj.build_info); -} - -void Component::Loader::install_map() -{ - load_sub(obj.install_map, obj.package.get_source_directory()); -} - -void Component::Loader::overlay(const string &o) -{ - obj.overlays.push_back(o); -} - -void Component::Loader::require(const string &n) -{ - Package *req = obj.package.get_builder().get_package_manager().find_package(n); - if(req) - obj.requires.push_back(req); - else - obj.problems.push_back(format("Required package %s not found", n)); -} - -void Component::Loader::source(const string &s) -{ - obj.sources.push_back((obj.package.get_source_directory()/s).str()); -} diff --git a/source/component.h b/source/component.h deleted file mode 100644 index 7ddd972..0000000 --- a/source/component.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef COMPONENT_H_ -#define COMPONENT_H_ - -#include -#include -#include -#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, 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 sources; - std::vector overlays; - bool install = false; - BuildInfo build_info; - Package::Requirements requires; - bool deflt = true; - InstallMap install_map; - std::vector 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 &get_sources() const { return sources; } - - const std::vector &get_overlays() const { return overlays; } - -protected: - /** Returns a list of all source files for the component. */ - std::vector 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 &get_problems() const { return problems; } - - /** Prepares any required packages. */ - void prepare(); - - /** Prepares the build information for building. Pulls build info from the - parent and dependency packages, and adds any component-specific flags. */ - virtual void create_build_info(); - - virtual void update_exported_build_info(BuildInfo &) const { } - - const BuildInfo &get_build_info() const { return build_info; } - - BuildInfo get_build_info_for_path(const Msp::FS::Path &) const; - - virtual void create_targets() const = 0; -}; - -#endif diff --git a/source/conditionalloader.cpp b/source/conditionalloader.cpp deleted file mode 100644 index b9d9d25..0000000 --- a/source/conditionalloader.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "booleanevaluator.h" -#include "builder.h" -#include "conditionalloader.h" -#include "sourcepackage.h" - -using namespace std; -using namespace Msp; - -ArchitectureConditional::ArchitectureConditional(const Builder &b, const string &l): - builder(b), - log_prefix(l) -{ - add("if_arch", &ArchitectureConditional::if_arch); -} - -void ArchitectureConditional::if_arch(const string &cond) -{ - const Architecture &arch = builder.get_current_arch(); - BooleanEvaluator eval([&arch](const string &value){ return arch.match_name(value); }); - bool match = eval.evaluate(cond); - builder.get_logger().log("configure", "%s: arch %s %smatched", log_prefix, cond, (match ? "" : "not ")); - if(match) - load_sub_with(*this); -} - - -FeatureConditional::FeatureConditional(const SourcePackage &p, const string &l): - package(p), - log_prefix(l) -{ - add("if_feature", &FeatureConditional::if_feature); -} - -void FeatureConditional::if_feature(const string &cond) -{ - BooleanEvaluator eval([this](const string &feat, const string *value){ return package.match_feature(feat, value); }); - bool match = eval.evaluate(cond); - package.get_builder().get_logger().log("configure", "%s: feature %s %smatched", log_prefix, cond, (match ? "" : "not ")); - if(match) - load_sub_with(*this); -} - - -ConditionalLoader::ConditionalLoader(const SourcePackage &p, const string &l): - ArchitectureConditional(p.get_builder(), l), - FeatureConditional(p, l) -{ } diff --git a/source/conditionalloader.h b/source/conditionalloader.h deleted file mode 100644 index 5184fac..0000000 --- a/source/conditionalloader.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef CONDITIONALLOADER_H_ -#define CONDITIONALLOADER_H_ - -#include -#include - -class Builder; -class SourcePackage; - -class ArchitectureConditional: virtual public Msp::DataFile::Loader -{ -private: - const Builder &builder; - std::string log_prefix; - -protected: - ArchitectureConditional(const Builder &, const std::string &); - -private: - void if_arch(const std::string &); -}; - - -class FeatureConditional: virtual public Msp::DataFile::Loader -{ -private: - const SourcePackage &package; - std::string log_prefix; - -protected: - FeatureConditional(const SourcePackage &, const std::string &); - - void if_feature(const std::string &); -}; - - -class ConditionalLoader: public ArchitectureConditional, FeatureConditional -{ -protected: - ConditionalLoader(const SourcePackage &, const std::string &); -}; - -#endif diff --git a/source/config.cpp b/source/config.cpp deleted file mode 100644 index 1d3b180..0000000 --- a/source/config.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#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(c) -{ - add("option", &Loader::option); -} - -void Config::Loader::option(const string &n, const string &v) -{ - if(obj.options.count(n)) - obj.set_option(n, v); - else - obj.pending_options[n] = v; -} diff --git a/source/config.h b/source/config.h deleted file mode 100644 index ba8416b..0000000 --- a/source/config.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef CONFIG_H_ -#define CONFIG_H_ - -#include -#include -#include -#include -#include -#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; - -private: - class Loader: public Msp::DataFile::ObjectLoader - { - public: - Loader(Config &); - private: - void option(const std::string &, const std::string &); - }; - - SourcePackage &package; - std::map 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 &get_options() const { return options; } - const Msp::Time::TimeStamp &get_mtime() const { return mtime; } - - void load(); - void save() const; -}; - -#endif diff --git a/source/copy.cpp b/source/copy.cpp deleted file mode 100644 index 8f6b81d..0000000 --- a/source/copy.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef _WIN32 -#include -#include -#endif -#include -#include -#include -#include -#include -#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 &sources, const string &arg) -{ - FileTarget &file_tgt = dynamic_cast(*sources.front()); - InstalledFile *inst = new InstalledFile(builder, *file_tgt.get_package(), file_tgt, arg); - inst->set_tool(*this); - return inst; -} - -bool Copy::_run(const InstalledFile &install) -{ - const FileTarget &source = install.get_source(); - const FS::Path &src_path = source.get_path(); - const FS::Path &dst_path = install.get_path(); - - try - { - IO::File in(src_path.str()); - IO::File out(dst_path.str(), IO::M_WRITE); - - // Actual transfer loop - char buf[16384]; - while(!in.eof()) - { - unsigned len = in.read(buf, sizeof(buf)); - out.write(buf, len); - } - } - catch(const exception &e) - { - IO::print(IO::cerr, "%s\n", e.what()); - return false; - } - -#ifndef _WIN32 - // Preserve file permissions - struct stat st; - if(stat(src_path.str().c_str(), &st)==0) - chmod(dst_path.str().c_str(), st.st_mode&0777); - - const FS::Path &link = install.get_symlink(); - if(!link.empty()) - { - FS::Path relpath = FS::relative(dst_path, FS::dirname(link)); - if(FS::exists(link)) - FS::unlink(link); - symlink(relpath.str().c_str(), link.str().c_str()); - } -#endif - - return true; -} diff --git a/source/copy.h b/source/copy.h deleted file mode 100644 index d65bb05..0000000 --- a/source/copy.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef COPY_H_ -#define COPY_H_ - -#include "tool.h" - -class InstalledFile; - -/** -Copies a file to another place. Used by the InstalledFile target. -*/ -class Copy: public Tool -{ -public: - Copy(Builder &); - - Target *create_target(const std::vector &, const std::string &) override; - -private: - static bool _run(const InstalledFile &); -}; - -#endif diff --git a/source/csourcefile.cpp b/source/csourcefile.cpp deleted file mode 100644 index bc76605..0000000 --- a/source/csourcefile.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include -#include -#include -#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(mtimeget_build_info_for_path(path); - const auto &incpath = build_info.incpath; - VirtualFileSystem::SearchPath local_incpath; - local_incpath.reserve(1+build_info.local_incpath.size()+incpath.size()); - local_incpath.push_back(FS::dirname(path).str()); - local_incpath.insert(local_incpath.end(), build_info.local_incpath.begin(), build_info.local_incpath.end()); - local_incpath.insert(local_incpath.end(), incpath.begin(), incpath.end()); - - Tool *compiler = builder.get_toolchain().get_tool_for_suffix(FS::extpart(FS::basename(path)), true); - if(compiler) - compiler->prepare(); - for(const string &i: includes) - if(Target *hdr = builder.get_vfs().find_header(i.substr(1), compiler, (i[0]=='"' ? local_incpath : incpath))) - add_transitive_dependency(*hdr); -} - -void CSourceFile::modified() -{ - includes.clear(); - trans_depends.clear(); - find_dependencies(); -} diff --git a/source/csourcefile.h b/source/csourcefile.h deleted file mode 100644 index c57654a..0000000 --- a/source/csourcefile.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef CSOURCEFILE_H_ -#define CSOURCEFILE_H_ - -#include -#include "sourcefile.h" - -/** -Represents a C or C++ source file. -*/ -class CSourceFile: public SourceFile -{ -protected: - std::vector 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 &get_includes() const { return includes; } -protected: - virtual void parse_includes(Msp::IO::Base &); - void find_dependencies() override; - void modified() override; -}; - -#endif diff --git a/source/customizedtool.cpp b/source/customizedtool.cpp deleted file mode 100644 index 9171996..0000000 --- a/source/customizedtool.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "builder.h" -#include "customizedtool.h" -#include "target.h" - -using namespace std; -using namespace Msp; - -CustomizedTool::CustomizedTool(Builder &b, const std::string &t, const Architecture &a): - CustomizedTool(b.get_toolchain().get_tool(t), a) -{ } - -CustomizedTool::CustomizedTool(Tool &t, const Architecture &a): - Tool(t.get_builder(), &a, t.get_tag()), - parent(t) -{ - input_suffixes = parent.get_input_suffixes(); - aux_suffixes = parent.get_auxiliary_suffixes(); - processing_unit = parent.get_processing_unit(); - - set_run([this](const Target &tgt){ return parent.run(tgt); }); -} - -Target *CustomizedTool::create_source(const Component &c, const FS::Path &p) const -{ - return parent.create_source(c, p); -} - -Target *CustomizedTool::create_source(const FS::Path &p) const -{ - return parent.create_source(p); -} - -Target *CustomizedTool::create_target(const vector &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)); -} diff --git a/source/customizedtool.h b/source/customizedtool.h deleted file mode 100644 index 55c4c6a..0000000 --- a/source/customizedtool.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef CUSTOMIZEDTOOL_H_ -#define CUSTOMIZEDTOOL_H_ - -#include "tool.h" - -class CustomizedTool: public Tool -{ -protected: - Tool &parent; - - CustomizedTool(Builder &, const std::string &, const Architecture &); - CustomizedTool(Tool &, const Architecture &); - -public: - const Tool *get_base_tool() const override { return &parent; } - Target *create_source(const Component &, const Msp::FS::Path &) const override; - Target *create_source(const Msp::FS::Path &) const override; - Target *create_target(const std::vector &, const std::string & = std::string()) override; - Target *create_install(Target &) const override; - std::string create_build_signature(const BuildInfo &) const override; -protected: - void do_prepare(ToolData &) const override; -}; - -#endif diff --git a/source/datacollection.cpp b/source/datacollection.cpp deleted file mode 100644 index c34c9cc..0000000 --- a/source/datacollection.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include "component.h" -#include "datacollection.h" -#include "datatransform.h" -#include "sourcepackage.h" - -using namespace Msp; - -DataCollection::DataCollection(Builder &b, const Component &c, DataTransform &s): - FileTarget(b, c.get_package(), generate_target_path(c, s.get_path())), - source(s) -{ - component = &c; - add_dependency(source); -} - -Msp::FS::Path DataCollection::generate_target_path(const Component &comp, const Msp::FS::Path &src) -{ - return comp.get_package().get_temp_directory()/comp.get_name()/(FS::basepart(FS::basename(src))+".mdc"); -} - -void DataCollection::find_dependencies() -{ - source.prepare(); - for(Target *d: source.get_transitive_dependencies()) - add_dependency(*d); -} diff --git a/source/datacollection.h b/source/datacollection.h deleted file mode 100644 index aeb270f..0000000 --- a/source/datacollection.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef DATACOLLECTION_H_ -#define DATACOLLECTION_H_ - -#include "filetarget.h" - -class DataTransform; - -class DataCollection: public FileTarget -{ -private: - DataTransform &source; - -public: - DataCollection(Builder &, const Component &, DataTransform &); -private: - static Msp::FS::Path generate_target_path(const Component &, const Msp::FS::Path &); - -public: - const char *get_type() const override { return "DataCollection"; } - DataTransform &get_source() const { return source; } - -private: - void find_dependencies() override; -}; - -#endif diff --git a/source/datapack.cpp b/source/datapack.cpp deleted file mode 100644 index 713120f..0000000 --- a/source/datapack.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "component.h" -#include "datapack.h" -#include "sourcepackage.h" - -using namespace std; - -DataPack::DataPack(Builder &b, const Component &c, const vector &f): - FileTarget(b, c.get_package(), generate_target_path(c)), - files(f) -{ - component = &c; - for(FileTarget *t: files) - add_dependency(*t); - - install_location = Msp::FS::Path("share")/package->get_name(); -} - -Msp::FS::Path DataPack::generate_target_path(const Component &comp) -{ - return comp.get_package().get_output_directory()/(comp.get_name()+".mdp"); -} diff --git a/source/datapack.h b/source/datapack.h deleted file mode 100644 index b02922f..0000000 --- a/source/datapack.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef DATAPACK_H_ -#define DATAPACK_H_ - -#include "filetarget.h" - -class DataPack: public FileTarget -{ -private: - std::vector files; - -public: - DataPack(Builder &, const Component &, const std::vector &); -private: - static Msp::FS::Path generate_target_path(const Component &); - -public: - const char *get_type() const override { return "DataPack"; } - - const std::vector &get_files() const { return files; } -}; - -#endif diff --git a/source/datapackcomponent.cpp b/source/datapackcomponent.cpp deleted file mode 100644 index 5750287..0000000 --- a/source/datapackcomponent.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include -#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 files; - for(const FS::Path &s: collect_source_files()) - { - string ext = FS::extpart(FS::basename(s)); - if(ext==".mdt") - { - Target *src = dcomp.create_source(*this, s); - files.push_back(dcomp.create_target(*src, "collection")); - } - else if(Target *tgt = builder.get_vfs().get_target(s)) - files.push_back(tgt); - else - files.push_back(new DataSourceFile(builder, *this, s)); - } - - Target *result = dcomp.create_target(files, "pack"); - - BuildGraph &build_graph = builder.get_build_graph(); - build_graph.add_primary_target(*result); - if(install) - build_graph.add_installed_target(*result); -} diff --git a/source/datapackcomponent.h b/source/datapackcomponent.h deleted file mode 100644 index 75eda73..0000000 --- a/source/datapackcomponent.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef DATAPACKCOMPONENT_H_ -#define DATAPACKCOMPONENT_H_ - -#include "component.h" - -class DataPackComponent: public Component -{ -public: - DataPackComponent(SourcePackage &, const std::string &); - - void create_targets() const override; -}; - -#endif diff --git a/source/datasourcefile.h b/source/datasourcefile.h deleted file mode 100644 index 7518423..0000000 --- a/source/datasourcefile.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef DATASOURCEFILE_H_ -#define DATASOURCEFILE_H_ - -#include "sourcefile.h" - -class DataSourceFile: public SourceFile -{ -public: - DataSourceFile(Builder &b, const Msp::FS::Path &p): SourceFile(b, p) { } - DataSourceFile(Builder &b, const Component &c, const Msp::FS::Path &p): SourceFile(b, c, p) { } - - const char *get_type() const override { return "DataSourceFile"; } -}; - -#endif diff --git a/source/datatool.cpp b/source/datatool.cpp deleted file mode 100644 index 1bc629e..0000000 --- a/source/datatool.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include -#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 &sources, const string &arg) -{ - if(arg=="collection") - { - if(sources.size()!=1) - throw invalid_argument("DataTool::create_target"); - DataTransform &source = dynamic_cast(*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 files; - files.reserve(sources.size()); - for(Target *t: sources) - files.push_back(&dynamic_cast(*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 argv; - argv.push_back(tool.get_executable()->get_path().str()); - - argv.push_back("-o"); - argv.push_back(FS::relative(dynamic_cast(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(&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(&tgt)) - { - argv.push_back("-p"); - for(const FileTarget *f: pack->get_files()) - argv.push_back(FS::relative(f->get_path(), work_dir).str()); - } - - return new ExternalTask(argv, work_dir); -} diff --git a/source/datatool.h b/source/datatool.h deleted file mode 100644 index 1cddebb..0000000 --- a/source/datatool.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef DATACOMPILER_H_ -#define DATACOMPILER_H_ - -#include "tool.h" - -class DataTool: public Tool -{ -public: - DataTool(Builder &); - - Target *create_source(const Component &, const Msp::FS::Path &) const override; - Target *create_target(const std::vector &, const std::string &) override; - std::string create_build_signature(const BuildInfo &) const override; - -private: - static Task *_run(const Target &); -}; - -#endif diff --git a/source/datatransform.cpp b/source/datatransform.cpp deleted file mode 100644 index 01d237b..0000000 --- a/source/datatransform.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include -#include -#include -#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 files; - Cache &cache = component->get_package().get_cache(); - const Time::TimeStamp &cache_mtime = cache.get_mtime(); - if(mtime 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()); - 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()); - } - - cache.set_values(this, "files", files); - } - - for(const string &f: files) - { - FS::Path file_path = FS::dirname(path)/f; - if(Target *tgt = builder.get_vfs().get_target(file_path)) - add_transitive_dependency(*tgt); - else - add_transitive_dependency(*new File(builder, *package, file_path)); - } -} diff --git a/source/datatransform.h b/source/datatransform.h deleted file mode 100644 index f024973..0000000 --- a/source/datatransform.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef DATATRANSFORM_H_ -#define DATATRANSFORM_H_ - -#include "filetarget.h" - -class DataTransform: public FileTarget -{ -private: - Msp::Time::TimeStamp dir_mtime; - -public: - DataTransform(Builder &, const Component &, const Msp::FS::Path &); - - const char *get_type() const override { return "DataTransform"; } - -private: - void find_dependencies() override; -}; - -#endif diff --git a/source/executable.cpp b/source/executable.cpp deleted file mode 100644 index aa0d8fb..0000000 --- a/source/executable.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "builder.h" -#include "component.h" -#include "executable.h" -#include "sourcepackage.h" - -using namespace std; -using namespace Msp; - -Executable::Executable(Builder &b, const Component &c, const vector &objs): - Binary(b, c, b.get_current_arch().create_filename(c.get_name()), objs) -{ - install_location = "bin"; -} diff --git a/source/executable.h b/source/executable.h deleted file mode 100644 index 5728e2f..0000000 --- a/source/executable.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef EXECUTABLE_H_ -#define EXECUTABLE_H_ - -#include "binary.h" - -class Executable: public Binary -{ -public: - Executable(Builder &b, const Msp::FS::Path &p): Binary(b, p) { } - Executable(Builder &, const Component &, const std::vector &); - - const char *get_type() const override { return "Executable"; } -}; - -#endif diff --git a/source/exportdefinitions.cpp b/source/exportdefinitions.cpp deleted file mode 100644 index 7edf759..0000000 --- a/source/exportdefinitions.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "component.h" -#include "exportdefinitions.h" -#include "objectfile.h" -#include "sourcepackage.h" - -using namespace std; -using namespace Msp; - -ExportDefinitions::ExportDefinitions(Builder &b, const Component &c, const vector &objs): - FileTarget(b, c.get_package(), generate_target_path(c)) -{ - component = &c; - for(ObjectFile *o: objs) - add_dependency(*o); -} - -FS::Path ExportDefinitions::generate_target_path(const Component &comp) -{ - return comp.get_package().get_temp_directory()/comp.get_name()/(comp.get_name()+".def"); -} diff --git a/source/exportdefinitions.h b/source/exportdefinitions.h deleted file mode 100644 index b8631ee..0000000 --- a/source/exportdefinitions.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef EXPORTDEFINITIONS_H_ -#define EXPORTDEFINITIONS_H_ - -#include "filetarget.h" - -class ObjectFile; - -/** -An export definition file for a shared library. Only used on Windows. -*/ -class ExportDefinitions: public FileTarget -{ -public: - ExportDefinitions(Builder &, const Component &, const std::vector &); -private: - static Msp::FS::Path generate_target_path(const Component &); - -public: - const char *get_type() const override { return "ExportDefinitions"; } -}; - -#endif diff --git a/source/externaltask.cpp b/source/externaltask.cpp deleted file mode 100644 index 37a36d3..0000000 --- a/source/externaltask.cpp +++ /dev/null @@ -1,195 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "externaltask.h" - -using namespace std; -using namespace Msp; - -ExternalTask::ExternalTask(const Arguments &a, const FS::Path &wd): - argv(a), - work_dir(wd) -{ - if(argv.empty()) - throw invalid_argument("ExternalTask::ExternalTask"); -} - -ExternalTask::~ExternalTask() -{ - delete capture_pipe; -} - -string ExternalTask::get_command() const -{ - string cmd; - for(const string &a: argv) - { - if(!cmd.empty()) - cmd += ' '; - - for(char c: a) - { - if(c=='"' || c=='\'' || c==' ' || c=='\\' || c=='&') - cmd += '\\'; - cmd += c; - } - } - - if(stdin_action==REDIRECT) - { - cmd += " <"; - cmd += stdin_file.str(); - } - - if(stdout_action==REDIRECT) - { - cmd += " >"; - cmd += stdout_file.str(); - } - - return cmd; -} - -void ExternalTask::start() -{ - IO::File *devnull = 0; - IO::File *infile = 0; - IO::File *outfile = 0; - - prepare(); - - process = new Process; - - if(stdin_action==IGNORE || stdout_action==IGNORE || stderr_action==IGNORE) - { -#ifdef _WIN32 - devnull = new IO::File("nul", IO::M_RDWR); -#else - devnull = new IO::File("/dev/null", IO::M_RDWR); -#endif - if(stdin_action==IGNORE) - process->redirect_cin(*devnull); - if(stdout_action==IGNORE) - process->redirect_cout(*devnull); - if(stderr_action==IGNORE) - process->redirect_cerr(*devnull); - } - - if(stdout_action==REDIRECT) - { - outfile = new IO::File((work_dir/stdout_file).str(), IO::M_WRITE); - process->redirect_cout(*outfile); - } - - if(stdout_action==CAPTURE || stderr_action==CAPTURE) - { - capture_pipe = new IO::Pipe; - if(stdout_action==CAPTURE) - process->redirect_cout(*capture_pipe); - if(stderr_action==CAPTURE) - process->redirect_cerr(*capture_pipe); - } - - if(stdin_action==REDIRECT) - { - infile = new IO::File((work_dir/stdin_file).str()); - process->redirect_cin(*infile); - } - - if(!work_dir.empty()) - process->set_working_directory(work_dir); - - Process::Arguments args(argv.begin()+1, argv.end()); - process->execute(argv.front(), args); - if(capture_pipe) - capture_pipe->set_mode(IO::M_READ); - - delete devnull; - delete infile; - delete outfile; -} - -Task::Status ExternalTask::check() -{ - return do_wait(false); -} - -Task::Status ExternalTask::wait() -{ - return do_wait(true); -} - -Task::Status ExternalTask::do_wait(bool block) -{ - while(process) - { - if(process->wait(block && !capture_pipe)) - { - exit_code = process->get_exit_code(); - delete process; - process = 0; - } - - // Do this after waiting to avoid a race condition - while(capture_pipe && IO::poll(*capture_pipe, IO::P_INPUT, 10*Time::msec)) - { - char buf[1024]; - unsigned len = capture_pipe->read(buf, sizeof(buf)); - if(len) - output.append(buf, len); - else - break; - } - - if(process) - { - if(!block) - return RUNNING; - } - else - signal_finished.emit(!exit_code); - } - - return exit_code ? ERROR : SUCCESS; -} - -void ExternalTask::set_stdin(const FS::Path &f) -{ - stdin_action = REDIRECT; - stdin_file = f; -} - -void ExternalTask::set_stdout(StreamAction a) -{ - if(a==REDIRECT) - throw invalid_argument("ExternalTask::set_stdout"); - stdout_action = a; -} - -void ExternalTask::set_stdout(const FS::Path &f) -{ - stdout_action = REDIRECT; - stdout_file = f; -} - -void ExternalTask::set_stderr(StreamAction a) -{ - if(a==REDIRECT) - throw invalid_argument("ExternalTask::set_stdout"); - stderr_action = a; -} - -string ExternalTask::run_and_capture_output(const Arguments &argv, const FS::Path &wd, bool capture_stderr) -{ - ExternalTask task(argv, wd); - task.stdin_action = IGNORE; - task.set_stdout(CAPTURE); - task.set_stderr(capture_stderr ? CAPTURE : IGNORE); - task.start(); - if(task.wait()!=SUCCESS) - throw runtime_error(format("%s failed", argv.front())); - return task.get_output(); -} diff --git a/source/externaltask.h b/source/externaltask.h deleted file mode 100644 index 9b9bc43..0000000 --- a/source/externaltask.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef EXTERNALTASK_H_ -#define EXTERNALTASK_H_ - -#include -#include -#include -#include -#include -#include "task.h" - -/** -Runs an external command. A zero exit status is translated to a SUCCESS status -for the task, and anything else is treated as an error. Output can optionally -be captured. -*/ -class ExternalTask: public Task -{ -public: - enum StreamAction - { - PASSTHROUGH, //< Do not touch the stream - CAPTURE, //< Capture the stream - REDIRECT, //< Redirect the stream to/from a file - IGNORE //< Redirect the stream to oblivion - }; - - using Arguments = Msp::Process::Arguments; - -private: - Arguments argv; - Msp::FS::Path work_dir; - Msp::Process *process = 0; - int exit_code = -1; - StreamAction stdin_action = PASSTHROUGH; - Msp::FS::Path stdin_file; - StreamAction stdout_action = PASSTHROUGH; - Msp::FS::Path stdout_file; - StreamAction stderr_action = PASSTHROUGH; - Msp::IO::Pipe *capture_pipe = 0; - std::string output; - -public: - /** Creates an ExternalTask with an argument array and an optional working - directory. The first element of the argument array should be the command - name. If the working directory is not specified, no chdir is done. */ - ExternalTask(const Arguments &, const Msp::FS::Path & = Msp::FS::Path()); - - ~ExternalTask(); - - std::string get_command() const override; - void start() override; - Status check() override; - Status wait() override; -private: - Status do_wait(bool); - -public: - /// Redirect stdin from a file. Has no effect after the task is started. - void set_stdin(const Msp::FS::Path &); - - /// Sets destination for stdout. Has no effect after the task is started. - void set_stdout(StreamAction); - - /// Redirect stdout to a file. Has no effect after the task is started. - void set_stdout(const Msp::FS::Path &); - - /// Sets destination for stderr. Has no effect after the task is started. - void set_stderr(StreamAction); - - /** Returns captured output, if any. This may be called while the task is - still running, but it will always return all output. */ - const std::string &get_output() const { return output; } - - /** Executes a command and captures its output. If the command exits with - a nonzero status, an exception is thrown. */ - static std::string run_and_capture_output(const Arguments &, const Msp::FS::Path & = Msp::FS::Path(), bool = false); -}; - -#endif diff --git a/source/feature.cpp b/source/feature.cpp deleted file mode 100644 index 2518377..0000000 --- a/source/feature.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "feature.h" - -using namespace std; -using namespace Msp; - -Feature::Loader::Loader(Feature &f): - Msp::DataFile::ObjectLoader(f) -{ - add("choice", &Loader::choice); - add("description", &Feature::description); - add("default", &Feature::default_value); - add("export", &Feature::exported); -} - -void Feature::Loader::choice(const string &c) -{ - if(obj.choices.empty()) - obj.default_value = c; - obj.choices.push_back(c); -} diff --git a/source/feature.h b/source/feature.h deleted file mode 100644 index 0dca1f5..0000000 --- a/source/feature.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef FEATURE_H_ -#define FEATURE_H_ - -#include - -struct Feature -{ - class Loader: public Msp::DataFile::ObjectLoader - { - public: - Loader(Feature &); - - private: - void choice(const std::string &); - }; - - std::string name; - std::string description; - std::string default_value = "no"; - std::vector choices; - bool exported = false; - - Feature(const std::string &n): name(n) { } -}; - -#endif diff --git a/source/file.h b/source/file.h deleted file mode 100644 index 42d73b8..0000000 --- a/source/file.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FILE_H_ -#define FILE_H_ - -#include "filetarget.h" - -/** -Just an arbitrary file. No special meaning attached. -*/ -class File: public FileTarget -{ -public: - File(Builder &b, const Msp::FS::Path &t): FileTarget(b, t) { } - File(Builder &b, const SourcePackage &p, const Msp::FS::Path &t): FileTarget(b, p, t) { } - - const char *get_type() const override { return "File"; } -}; - -#endif diff --git a/source/filetarget.cpp b/source/filetarget.cpp deleted file mode 100644 index a2bd3f0..0000000 --- a/source/filetarget.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include -#include -#include -#include -#include -#include -#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 ""+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(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 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 seen_tools; - vector tool_sigs; - for(Target *d: depends) - if(const Tool *t = d->get_tool()) - if(d->get_component()==component && !any_equals(seen_tools, t)) - { - seen_tools.push_back(t); - tool_sigs.push_back(t->create_build_signature(binfo)); - } - - sort(tool_sigs); - sigs.insert(sigs.end(), make_move_iterator(tool_sigs.begin()), make_move_iterator(tool_sigs.end())); - } - - return join(sigs.begin(), sigs.end(), ";"); -} - -void FileTarget::build(Task &task) -{ - task.add_file(path); - task.set_unlink(true); -} - -void FileTarget::build_finished(bool success) -{ - if(success) - { - stat(); - if(package) - { - string build_sig = create_build_signature(); - if(!build_sig.empty()) - package->get_cache().set_value(this, "build_sig", build_sig); - } - } - - Target::build_finished(success); -} - -void FileTarget::clean() -{ - if(mtime) - { - FS::unlink(path); - mtime = Time::TimeStamp(); - size = 0; - check_rebuild(); - } -} diff --git a/source/filetarget.h b/source/filetarget.h deleted file mode 100644 index da165d0..0000000 --- a/source/filetarget.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef FILETARGET_H_ -#define FILETARGET_H_ - -#include -#include "target.h" - -/** -An intermediate base class for targets that represent files. Almost all target -classes are derived from this. -*/ -class FileTarget: public Target -{ -protected: - Msp::FS::Path path; - Msp::Time::TimeStamp mtime; - unsigned size = 0; - Msp::FS::Path install_location; - std::string install_filename; - bool nested_build_sig = false; - bool arch_in_build_sig = false; - - FileTarget(Builder &b, const Msp::FS::Path &a): FileTarget(b, 0, a) { } - FileTarget(Builder &b, const SourcePackage &p, const Msp::FS::Path &a): FileTarget(b, &p, a) { } -private: - FileTarget(Builder &, const SourcePackage *, const Msp::FS::Path &); - void stat(); - static std::string generate_name(Builder &, const SourcePackage *, const Msp::FS::Path &); - -public: - const Msp::FS::Path &get_path() const { return path; } - const Msp::Time::TimeStamp &get_mtime() const { return mtime; } - unsigned get_size() const { return size; } - - bool is_installable() const { return !install_location.empty(); } - const Msp::FS::Path &get_install_location() const { return install_location; } - const std::string &get_install_filename() const { return install_filename; } - - /// Changes the mtime of the target to the current time. - void touch(); - -protected: - void check_rebuild() override; - - virtual std::string create_build_signature() const; - - void build(Task &) override; - - void build_finished(bool) override; - -public: - void clean() override; -}; - -#endif diff --git a/source/gnuarchiver.cpp b/source/gnuarchiver.cpp deleted file mode 100644 index dc0b784..0000000 --- a/source/gnuarchiver.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include -#include -#include -#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 &sources, const string &) -{ - if(sources.empty()) - throw invalid_argument("GnuArchiver::create_target"); - - vector objs; - objs.reserve(sources.size()); - for(Target *s: sources) - objs.push_back(&dynamic_cast(*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 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(d)) - argv.push_back(relative(obj->get_path(), work_dir).str()); - - return argv; -} diff --git a/source/gnuarchiver.h b/source/gnuarchiver.h deleted file mode 100644 index 4305827..0000000 --- a/source/gnuarchiver.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef GNUARCHIVER_H_ -#define GNUARCHIVER_H_ - -#include "tool.h" - -class StaticLibrary; - -class GnuArchiver: public Tool -{ -public: - GnuArchiver(Builder &, const Architecture &); - - Target *create_target(const std::vector &, const std::string &) override; - -private: - static ExternalTask::Arguments _run(const StaticLibrary &, Msp::FS::Path &); -}; - -#endif diff --git a/source/gnucompiler.cpp b/source/gnucompiler.cpp deleted file mode 100644 index 3236631..0000000 --- a/source/gnucompiler.cpp +++ /dev/null @@ -1,353 +0,0 @@ -#include -#include -#include -#include -#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 &sources, const string &) -{ - if(sources.size()!=1) - throw invalid_argument("GnuCompiler::create_target"); - SourceFile &source = dynamic_cast(*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).get_tag(); - - const FileTarget *exe = static_cast(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 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).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).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 version_parts = split(version_str, '.'); - for(unsigned i=0; (i<3 && i(version_parts[i])<<(16-8*i); - } - catch(const runtime_error &) - { } - - return ver; -} - -ExternalTask::Arguments GnuCompiler::_run(const ObjectFile &object, FS::Path &work_dir) -{ - const Tool &tool = *object.get_tool(); - const Architecture &arch = *tool.get_architecture(); - - ExternalTask::Arguments argv; - argv.push_back(tool.get_executable()->get_path().str()); - argv.push_back("-c"); - - BuildInfo binfo; - object.collect_build_info(binfo); - - const std::string &tool_tag = tool.get_tag(); - string tag_for_std = (tool_tag=="OBJC" ? "CC" : tool_tag); - if(binfo.standards.count(tag_for_std)) - argv.push_back("-std="+get_item(binfo.standards, tag_for_std).str()); - if(tool_tag=="OBJC" && binfo.standards.count(tool_tag)) - argv.push_back("-fobjc-std="+get_item(binfo.standards, tool_tag).str()); - - if(binfo.warning_level>=1) - { - argv.push_back("-Wall"); - if(binfo.warning_level>=2) - { - argv.push_back("-Wextra"); - argv.push_back("-Wundef"); - unsigned version = tool.get_extra_data(); - if(version>=0x80000) - argv.push_back("-Wno-cast-function-type"); - } - if(binfo.warning_level>=3) - { - argv.push_back("-pedantic"); - argv.push_back("-Wno-long-long"); - argv.push_back("-Wshadow"); - if(tool_tag=="CC") - { - argv.push_back("-Wc++-compat"); - argv.push_back("-Wstrict-prototypes"); - } - } - if(binfo.warning_level>=4) - { - // Some truly paranoid warnings - argv.push_back("-Wstrict-overflow=4"); - argv.push_back("-Wfloat-equal"); - argv.push_back("-Wconversion"); - argv.push_back("-Wwrite-strings"); - argv.push_back("-Winline"); - } - if(binfo.fatal_warnings) - { - argv.push_back("-Werror"); - argv.push_back("-Wno-error=deprecated-declarations"); - } - } - - const FS::Path &sysroot = binfo.sysroot; - if(!sysroot.empty()) - argv.push_back("--sysroot="+sysroot.str()); - for(const FS::Path &p: binfo.local_incpath) - { - argv.push_back("-iquote"); - argv.push_back(p.str()); - } - for(const FS::Path &p: binfo.incpath) - argv.push_back("-I"+p.str()); - - for(const auto &kvp: binfo.defines) - { - if(kvp.second.empty()) - argv.push_back(format("-D%s", kvp.first)); - else - argv.push_back(format("-D%s=%s", kvp.first, kvp.second)); - } - - if(binfo.debug) - argv.push_back("-ggdb"); - if(binfo.optimize) - { - if(binfo.optimize<0) - argv.push_back("-Os"); - else - argv.push_back(format("-O%d", binfo.optimize)); - if(binfo.debug) - argv.push_back("-fno-omit-frame-pointer"); - } - if(binfo.threads && arch.get_system()!="windows" && arch.get_system()!="darwin") - argv.push_back("-pthread"); - if(object.is_used_in_shared_library() && arch.get_system()!="windows") - argv.push_back("-fPIC"); - - if((arch.get_type()=="x86" || arch.get_type()=="ppc") && !arch.is_native()) - argv.push_back(format("-m%d", arch.get_bits())); - - string cpu = arch.get_cpu(); - if(!cpu.empty()) - { - for(unsigned i=0; cpus[i]; i+=2) - if(cpu==cpus[i]) - { - cpu = cpus[i+1]; - break; - } - argv.push_back("-march="+cpu); - } - - if(!arch.get_fpu().empty()) - { - if(arch.get_type()=="x86") - { - if(arch.get_fpu()=="387") - argv.push_back("-mfpmath=387"); - else if(!arch.get_fpu().compare(0, 3, "sse")) - argv.push_back("-mfpmath=sse"); - - if(arch.get_fpu()=="sse") - argv.push_back("-msse2"); - else if(arch.get_fpu()=="sse3") - argv.push_back("-msse3"); - else if(arch.get_fpu()=="sse4.1") - argv.push_back("-msse4.1"); - } - else if(arch.get_type()=="arm") - { - argv.push_back("-mfpu="+arch.get_fpu()); - argv.push_back("-mfloat-abi=softfp"); - } - } - - FS::Path obj_path = object.get_path(); - FS::Path src_path = object.get_source().get_path(); - - argv.push_back("-o"); - argv.push_back(relative(obj_path, work_dir).str()); - argv.push_back(relative(src_path, work_dir).str()); - - return argv; -} diff --git a/source/gnucompiler.h b/source/gnucompiler.h deleted file mode 100644 index b0d0ec9..0000000 --- a/source/gnucompiler.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef GNUCOMPILER_H_ -#define GNUCOMPILER_H_ - -#include "tool.h" - -class ObjectFile; - -/** -Common base class for GNU compilers. Turns SourceFiles into ObjectFiles. - -Since invocation is mostly the same for all language frontends, most of the -logic is here and the individual tools only handle creating source files of -appropriate type. -*/ -class GnuCompiler: public Tool -{ -public: - GnuCompiler(Builder &, const Architecture &, const std::string &); - - Target *create_source(const Component &, const Msp::FS::Path &) const override; - Target *create_source(const Msp::FS::Path &) const override; - Target *create_target(const std::vector &, const std::string &) override; - std::string create_build_signature(const BuildInfo &) const override; -protected: - void do_prepare(ToolData &) const override; - void prepare_syspath(ToolData &) const; - void prepare_version(ToolData &) const; - unsigned query_version(const std::string &, const std::string &) const; - -private: - static ExternalTask::Arguments _run(const ObjectFile &, Msp::FS::Path &); -}; - -#endif diff --git a/source/gnulinker.cpp b/source/gnulinker.cpp deleted file mode 100644 index b726dd5..0000000 --- a/source/gnulinker.cpp +++ /dev/null @@ -1,325 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#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 &sources, const string &arg) -{ - if(sources.empty()) - throw invalid_argument("GnuLinker::create_target"); - vector objs; - objs.reserve(sources.size()); - for(Target *s: sources) - objs.push_back(&dynamic_cast(*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(&target)) - { - Tool © = builder.get_toolchain().get_tool("CP"); - InstalledFile *inst_tgt = dynamic_cast(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(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).get_tag(); - - const FileTarget *exe = static_cast(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(startis_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(&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(d); - Target *tgt = d->get_real_target(); - - if(ObjectFile *obj = dynamic_cast(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(tgt)) - argv.push_back((file?file:stlib)->get_path().str()); - else if(SharedLibrary *shlib = dynamic_cast(tgt)) - { - argv.push_back("-l"+shlib->get_libname()); - static_link_ok = false; - } - else if(ImportLibrary *imp = dynamic_cast(tgt)) - { - shlib = imp->get_shared_library(); - if(shlib) - argv.push_back("-l"+shlib->get_libname()); - else - argv.push_back((file?file:imp)->get_path().str()); - static_link_ok = false; - } - } - - for(const string &l: binfo.libs) - if(l.size()>10 && !l.compare(l.size()-10, 10, ".framework")) - { - argv.push_back("-framework"); - argv.push_back(l.substr(0, l.size()-10)); - } - - if(static_link_ok) - argv.push_back("-static"); - else - { - if(has_cplusplus) - { - auto i = binfo.libmodes.find("stdc++"); - if(i!=binfo.libmodes.end() && i->second<=BuildInfo::STATIC) - argv.push_back("-static-libstdc++"); - } - - if(arch.get_system()=="windows") - argv.push_back("-Wl,--enable-auto-import"); - } - - return argv; -} diff --git a/source/gnulinker.h b/source/gnulinker.h deleted file mode 100644 index a0632d6..0000000 --- a/source/gnulinker.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef GNULINKER_H_ -#define GNULINKER_H_ - -#include "tool.h" - -class Binary; - -/** -The GNU linker. Turns ObjectFiles into Executables and SharedLibraries. To -create a shared library, specify "shared" as the second argument to -create_target. - -Uses either gcc or g++ depending on what was used to compile the object files. -*/ -class GnuLinker: public Tool -{ -public: - GnuLinker(Builder &, const Architecture &); - - Target *create_target(const std::vector &, const std::string &) override; - Target *create_install(Target &) const override; - std::string create_build_signature(const BuildInfo &) const override; -protected: - void do_prepare(ToolData &) const override; -private: - static ExternalTask::Arguments _run(const Binary &, Msp::FS::Path &); -}; - -#endif diff --git a/source/gnutools.cpp b/source/gnutools.cpp deleted file mode 100644 index e64bcdb..0000000 --- a/source/gnutools.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "builder.h" -#include "gnuarchiver.h" -#include "gnucompiler.h" -#include "gnulinker.h" -#include "gnutools.h" -#include "mingwdlltool.h" - -GnuTools::GnuTools(Builder &builder, const Architecture &arch): - Toolchain("gnu", get_priority(arch)) -{ - add_tool(new GnuCompiler(builder, arch, "CC")); - add_tool(new GnuCompiler(builder, arch, "CXX")); - add_tool(new GnuCompiler(builder, arch, "OBJC")); - - add_tool(new GnuLinker(builder, arch)); - add_tool(new GnuArchiver(builder, arch)); - - if(arch.get_system()=="windows") - add_tool(new MingwDllTool(builder, arch)); -} - -int GnuTools::get_priority(const Architecture &arch) -{ - if(arch.get_toolchain()=="gnu") - return 20; - else if(arch.get_system()=="linux") - return 10; - else - return 0; -} diff --git a/source/gnutools.h b/source/gnutools.h deleted file mode 100644 index 5aeb966..0000000 --- a/source/gnutools.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef GNUTOOLS_H_ -#define GNUTOOLS_H_ - -#include "toolchain.h" - -class Architecture; -class Builder; - -class GnuTools: public Toolchain -{ -public: - GnuTools(Builder &, const Architecture &); - - static int get_priority(const Architecture &); -}; - -#endif diff --git a/source/importlibrary.cpp b/source/importlibrary.cpp deleted file mode 100644 index 435fb30..0000000 --- a/source/importlibrary.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#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(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(sl.get_libname()); -} diff --git a/source/importlibrary.h b/source/importlibrary.h deleted file mode 100644 index 1c5d682..0000000 --- a/source/importlibrary.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef IMPORTLIBRARY_H_ -#define IMPORTLIBRARY_H_ - -#include "filetarget.h" - -class ExportDefinitions; -class SharedLibrary; - -/** -A special case of static library which pulls in a shared library. Used on -platforms with no true dynamic linking support. -*/ -class ImportLibrary: public FileTarget -{ -private: - SharedLibrary *shared_lib = 0; - -public: - ImportLibrary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { } - ImportLibrary(Builder &, const Component &, SharedLibrary &, ExportDefinitions &); -private: - static std::string generate_filename(const Component &, const SharedLibrary &); - -public: - const char *get_type() const override { return "ImportLibrary"; } - - SharedLibrary *get_shared_library() const { return shared_lib; } -}; - -#endif diff --git a/source/installcomponent.cpp b/source/installcomponent.cpp deleted file mode 100644 index 86919fa..0000000 --- a/source/installcomponent.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "installcomponent.h" -#include "builder.h" -#include "file.h" -#include "sourcepackage.h" -#include "tool.h" - -using namespace std; -using namespace Msp; - -void InstallComponent::create_targets() const -{ - Builder &builder = package.get_builder(); - Target *inst = builder.get_build_graph().get_target("install"); - Tool © = builder.get_toolchain().get_tool("CP"); - - for(const FS::Path &s: collect_source_files()) - { - Target *tgt = builder.get_vfs().get_target(s); - if(!tgt) - tgt = new File(builder, package, s); - inst->add_dependency(*copy.create_target(*tgt, name)); - } -} diff --git a/source/installcomponent.h b/source/installcomponent.h deleted file mode 100644 index 457256d..0000000 --- a/source/installcomponent.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef INSTALLCOMPONENT_H_ -#define INSTALLCOMPONENT_H_ - -#include "component.h" - -class InstallComponent: public Component -{ -public: - InstallComponent(SourcePackage &p, const std::string &n): Component(p, n) { } - - void create_targets() const override; -}; - -#endif diff --git a/source/installedfile.cpp b/source/installedfile.cpp deleted file mode 100644 index 6e45ad2..0000000 --- a/source/installedfile.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include -#include "builder.h" -#include "installedfile.h" -#include "sharedlibrary.h" - -using namespace std; -using namespace Msp; - -InstalledFile::InstalledFile(Builder &b, const SourcePackage &p, FileTarget &s, const string &loc): - FileTarget(b, p, generate_target_path(b.get_prefix(), s, loc)), - source(s) -{ - add_dependency(source); -} - -FS::Path InstalledFile::generate_target_path(const FS::Path &global_prefix, const FileTarget &tgt, const string &loc) -{ - if(!tgt.is_installable() && loc.empty()) - throw invalid_argument(tgt.get_name()+" is not installable"); - - FS::Path prefix; - FS::Path mid; - if(!loc.compare(0, 2, "//")) - { - if(!tgt.get_package()) - throw invalid_argument("No private install location for "+tgt.get_name()); - - prefix = tgt.get_package()->get_temp_directory(); - mid = loc.substr(2); - } - else - { - prefix = global_prefix; - - if(!loc.empty()) - mid = loc; - else if(const Component *comp = tgt.get_component()) - mid = comp->get_install_map().get_install_location(tgt); - } - - if(mid.empty()) - mid = tgt.get_install_location(); - - string fn = tgt.get_install_filename(); - if(fn.empty()) - fn = FS::basename(tgt.get_path()); - - return prefix/mid/fn; -} - -void InstalledFile::set_symlink(const FS::Path &l) -{ - FS::Path al = FS::dirname(path)/l; - if(al==path) - throw invalid_argument("InstalledFile::set_symlink"); - link = FS::dirname(path)/l; - builder.get_vfs().register_path(link, this); -} - -Target *InstalledFile::get_real_target() -{ - return source.get_real_target(); -} - -void InstalledFile::check_rebuild() -{ - if(!mtime) - mark_rebuild("Does not exist"); - else if(source.get_mtime()>mtime || source.get_size()!=size) - mark_rebuild(source.get_name()+" has changed"); - else if(source.needs_rebuild()) - mark_rebuild(source.get_name()+" needs rebuilding"); - if(!needs_rebuild() && !link.empty()) - { - if(!FS::exists(link)) - mark_rebuild("Symlink does not exist"); - else - { - FS::Path rel_path = FS::relative(path, FS::dirname(link)); - if(FS::readlink(link)!=rel_path) - mark_rebuild("Symlink needs updating"); - } - } -} - -void InstalledFile::clean() -{ - if(!link.empty() && mtime) - FS::unlink(link); - FileTarget::clean(); -} diff --git a/source/installedfile.h b/source/installedfile.h deleted file mode 100644 index 0dd671f..0000000 --- a/source/installedfile.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef INSTALLEDFILE_H_ -#define INSTALLEDFILE_H_ - -#include "sourcepackage.h" -#include "filetarget.h" - -/** -Represents the installation of a file. -*/ -class InstalledFile: public FileTarget -{ -private: - FileTarget &source; - Msp::FS::Path link; - -public: - InstalledFile(Builder &, const SourcePackage &, FileTarget &, const std::string & =std::string()); -private: - static Msp::FS::Path generate_target_path(const Msp::FS::Path &, const FileTarget &i, const std::string &); - -public: - const char *get_type() const override { return "InstalledFile"; } - FileTarget &get_source() const { return source; } - - /** Sets a symlink for the file. A relative path will be rooted at the - directory the file resides in. */ - void set_symlink(const Msp::FS::Path &); - - const Msp::FS::Path &get_symlink() const { return link; } - - Target *get_real_target() override; -private: - void check_rebuild() override; - -public: - void clean() override; -}; - -#endif diff --git a/source/installmap.cpp b/source/installmap.cpp deleted file mode 100644 index 448f997..0000000 --- a/source/installmap.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#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(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(m), - source_base(s) -{ - add("map", &Loader::map); -} - -void InstallMap::Loader::map(const string &src, const string &inst) -{ - obj.add_mapping(source_base/src, inst); -} diff --git a/source/installmap.h b/source/installmap.h deleted file mode 100644 index 49211d7..0000000 --- a/source/installmap.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef INSTALLMAP_H_ -#define INSTALLMAP_H_ - -#include -#include -#include - -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 - { - 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 entries; - -public: - /** Adds an install mapping. Multiple mappings can be specified for the - same source location, but the first one that matches both that and the - target's default install location will be used. */ - void add_mapping(const Msp::FS::Path &, const Msp::FS::Path &); - - /** Returns the install location for a target. If no defined mappings match - the target, its default install location is returned. */ - Msp::FS::Path get_install_location(const FileTarget &) const; -}; - -#endif diff --git a/source/internaltask.cpp b/source/internaltask.cpp deleted file mode 100644 index fc929ce..0000000 --- a/source/internaltask.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "internaltask.h" - -InternalTask::~InternalTask() -{ - worker.join(); -} - -void InternalTask::start() -{ - prepare(); - worker.launch(); -} - -Task::Status InternalTask::check() -{ - Status result = worker.get_status(); - if(result!=RUNNING) - signal_finished.emit(result==SUCCESS); - return result; -} - -Task::Status InternalTask::wait() -{ - Status result; - while((result = check())==RUNNING) ; - return result; -} - - -void InternalTask::Worker::main() -{ - if(func()) - status = Task::SUCCESS; - else - status = Task::ERROR; -} diff --git a/source/internaltask.h b/source/internaltask.h deleted file mode 100644 index 608ea96..0000000 --- a/source/internaltask.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef INTERNALTASK_H_ -#define INTERNALTASK_H_ - -#include -#include -#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 func; - volatile Status status = Task::RUNNING; - - public: - Worker(std::function f): func(f) { } - - Status get_status() const { return status; } - - private: - void main() override; - }; - - Worker worker; - -public: - InternalTask(std::function f): worker(f) { } - ~InternalTask(); - - std::string get_command() const override { return ""; } - void start() override; - Status check() override; - Status wait() override; -}; - -#endif diff --git a/source/jarsigner.cpp b/source/jarsigner.cpp deleted file mode 100644 index a2a1c73..0000000 --- a/source/jarsigner.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#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 &, const string &) -{ - throw logic_error("not implemented"); -} - -ExternalTask::Arguments JarSigner::_run(const FileTarget &file, FS::Path &work_dir) -{ - const Tool &tool = *file.get_tool(); - - ExternalTask::Arguments argv; - argv.push_back(tool.get_executable()->get_path().str()); - - // TODO Make this generic - FS::Path home_dir = Msp::getenv("HOME"); - argv.push_back("-keystore"); - argv.push_back((home_dir/".android"/"debug.keystore").str()); - argv.push_back("-storepass"); - argv.push_back("android"); - - argv.push_back(FS::relative(file.get_path(), work_dir).str()); - argv.push_back("androiddebugkey"); - - return argv; -} diff --git a/source/jarsigner.h b/source/jarsigner.h deleted file mode 100644 index d836348..0000000 --- a/source/jarsigner.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef JARSIGNER_H_ -#define JARSIGNER_H_ - -#include "tool.h" - -class FileTarget; - -class JarSigner: public Tool -{ -public: - JarSigner(Builder &); - - Target *create_target(const std::vector &, const std::string &) override; - -private: - static ExternalTask::Arguments _run(const FileTarget &, Msp::FS::Path &); -}; - -#endif diff --git a/source/lib/architecture.cpp b/source/lib/architecture.cpp new file mode 100644 index 0000000..54ffcf0 --- /dev/null +++ b/source/lib/architecture.cpp @@ -0,0 +1,337 @@ +#include +#include +#include +#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::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("%.dll"); + if(toolchain=="msvc") + { + add_pattern("%.obj"); + add_pattern("%.lib"); + add_pattern("%_static.lib"); + } + else + { + add_pattern("%.o"); + add_pattern("lib%.dll"); + add_pattern("lib%.dll.a"); + add_pattern("lib%.a"); + } + add_pattern("%.exe"); + } + else + { + add_pattern("%.o"); + if(system=="darwin") + add_pattern("lib%.dylib"); + else + add_pattern("lib%.so"); + add_pattern("lib%.a"); + add_pattern("%"); + } +} + +bool Architecture::match_name(const string &pattern, unsigned *quality) const +{ + bool negate = (pattern[0]=='!'); + vector 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 &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 +void Architecture::add_pattern(const string &pat) +{ + filename_patterns[typeid(T).name()].push_back(Pattern(pat)); +} + +void Architecture::resolve_aliases(vector &parts) +{ + for(unsigned i=0; i 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 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(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(a) +{ + add("prefix", &Loader::cross_prefix); +} + +void Architecture::Loader::cross_prefix(const string &p) +{ + if(!obj.native) + obj.cross_prefix = p; +} diff --git a/source/lib/architecture.h b/source/lib/architecture.h new file mode 100644 index 0000000..2cf4fa2 --- /dev/null +++ b/source/lib/architecture.h @@ -0,0 +1,95 @@ +#ifndef ARCHITECTURE_H_ +#define ARCHITECTURE_H_ + +#include +#include +#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 + { + 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> 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 &) 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 + const std::vector &get_patterns() const; + + template + std::string create_filename(const std::string &) const; + +private: + template + void add_pattern(const std::string &); + +private: + static void resolve_aliases(std::vector &); + void parse_specification(const std::string &); +}; + +template +inline const std::vector &Architecture::get_patterns() const +{ + auto i = filename_patterns.find(typeid(T).name()); + if(i!=filename_patterns.end()) + return i->second; + + static std::vector empty; + return empty; +} + +template +inline std::string Architecture::create_filename(const std::string &base) const +{ + const std::vector &patterns = get_patterns(); + return patterns.empty() ? base : patterns.front().apply(base); +} + +#endif diff --git a/source/lib/binary.cpp b/source/lib/binary.cpp new file mode 100644 index 0000000..f245037 --- /dev/null +++ b/source/lib/binary.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#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 &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 static_libs; + vector shared_libs; + vector 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 &static_libs, vector &shared_libs, vector &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(real)) + { + /* Keep only the last occurrence of each static library. This + ensures the order is correct for linking. */ + auto i = find(static_libs, stlib); + if(i!=static_libs.end()) + static_libs.erase(i); + static_libs.push_back(stlib); + find_dependencies(stlib, static_libs, shared_libs, missing_libs); + } + else if(!any_equals(shared_libs, lib)) + shared_libs.push_back(lib); + } + else if(!any_equals(missing_libs, l)) + missing_libs.push_back(l); + } +} diff --git a/source/lib/binary.h b/source/lib/binary.h new file mode 100644 index 0000000..ac5a855 --- /dev/null +++ b/source/lib/binary.h @@ -0,0 +1,34 @@ +#ifndef BINARY_H_ +#define BINARY_H_ + +#include "buildinfo.h" +#include "filetarget.h" + +class Component; +class ObjectFile; + +/** +Produces a binary file, which may be either a standalone executable or a shared +library. +*/ +class Binary: public FileTarget +{ +private: + BuildInfo static_binfo; + +protected: + std::vector objects; + + Binary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { } + Binary(Builder &, const Component &, const std::string &, const std::vector &); + +public: + void collect_build_info(BuildInfo &) const override; + +protected: + void find_dependencies() override; +private: + void find_dependencies(Target *, std::vector &, std::vector &, std::vector &); +}; + +#endif diff --git a/source/lib/binarycomponent.cpp b/source/lib/binarycomponent.cpp new file mode 100644 index 0000000..3209ccc --- /dev/null +++ b/source/lib/binarycomponent.cpp @@ -0,0 +1,149 @@ +#include +#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 objs; + vector 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 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(*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(src)->is_installable()) + build_graph.add_installed_target(*src); + + for(Target *s: src->get_side_effects()) + if(dynamic_cast(s)->is_installable()) + build_graph.add_installed_target(*s); + } + } + } + + Tool &linker = toolchain.get_tool("LINK"); + + vector 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(c) +{ + add("use", &Loader::use); +} + +void BinaryComponent::Loader::use(const string &n) +{ + const BinaryComponent *comp = dynamic_cast(&obj.package.get_component(n)); + if(!comp || comp->type!=LIBRARY) + throw logic_error(n+" is not a library"); + + obj.uses.push_back(comp); +} diff --git a/source/lib/binarycomponent.h b/source/lib/binarycomponent.h new file mode 100644 index 0000000..ef145da --- /dev/null +++ b/source/lib/binarycomponent.h @@ -0,0 +1,38 @@ +#ifndef BINARYCOMPONENT_H_ +#define BINARYCOMPONENT_H_ + +#include "component.h" + +class BinaryComponent: public Component +{ +public: + class Loader: public Msp::DataFile::DerivedObjectLoader + { + public: + Loader(BinaryComponent &); + private: + void use(const std::string &); + }; + + enum Type + { + LIBRARY, + PROGRAM, + MODULE + }; + +private: + Type type; + std::vector uses; + +public: + BinaryComponent(SourcePackage &p, const std::string &n, Type t): Component(p, n), type(t) { } + + Type get_type() const { return type; } + + void create_build_info() override; + void update_exported_build_info(BuildInfo &) const override; + void create_targets() const override; +}; + +#endif diff --git a/source/lib/binarypackage.cpp b/source/lib/binarypackage.cpp new file mode 100644 index 0000000..adfca1a --- /dev/null +++ b/source/lib/binarypackage.cpp @@ -0,0 +1,169 @@ +#include +#include +#include +#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 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(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(p) +{ + add("build_info", &Loader::build_info); + add("header", &Loader::header); +} + +void BinaryPackage::Loader::build_info() +{ + load_sub(obj.export_binfo); +} + +void BinaryPackage::Loader::header(const string &h) +{ + obj.headers.push_back(h); +} diff --git a/source/lib/binarypackage.h b/source/lib/binarypackage.h new file mode 100644 index 0000000..62a8acf --- /dev/null +++ b/source/lib/binarypackage.h @@ -0,0 +1,40 @@ +#ifndef BINARYPACKAGE_H_ +#define BINARYPACKAGE_H_ + +#include "package.h" + +/** +Represents a package that is installed on the system, but can't be built by +Builder. +*/ +class BinaryPackage: public Package +{ +public: + class Loader: public Msp::DataFile::DerivedObjectLoader + { + public: + Loader(BinaryPackage &); + private: + void build_info(); + void header(const std::string &); + }; + + using Flags = std::vector; + +private: + Msp::FS::Path base_path; + std::vector headers; + BuildInfo static_binfo; + +public: + BinaryPackage(Builder &, const std::string &); + + const BuildInfo &get_static_build_info() const { return static_binfo; } + + static BinaryPackage *from_flags(Builder &, const std::string &, const Flags &, const Flags & = Flags()); +private: + static void process_flags(const Flags &, BuildInfo &); + void do_prepare() override; +}; + +#endif diff --git a/source/lib/booleanevaluator.cpp b/source/lib/booleanevaluator.cpp new file mode 100644 index 0000000..b7c399f --- /dev/null +++ b/source/lib/booleanevaluator.cpp @@ -0,0 +1,148 @@ +#include +#include +#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) +#include +#include + +class BooleanEvaluator +{ +public: + using ValueFunction = std::function; + using CompareFunction = std::function; + +private: + CompareFunction func; + std::string ops; + std::vector var_stack; + std::vector value_stack; + std::vector op_stack; + bool last_was_op; + +public: + BooleanEvaluator(const ValueFunction &); + BooleanEvaluator(const CompareFunction &); + + bool evaluate(const std::string &); +private: + void push_op(char); + bool pop_value(); + void collapse(unsigned); + unsigned precedence(char); + bool is_unary(char); + bool is_logic(char); +}; + +#endif diff --git a/source/lib/builder.cpp b/source/lib/builder.cpp new file mode 100644 index 0000000..92924eb --- /dev/null +++ b/source/lib/builder.cpp @@ -0,0 +1,364 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 Builder::get_build_types() const +{ + vector 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 Builder::collect_problems() const +{ + vector problems; + set broken_packages; + set broken_components; + set 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 tasks; + + unsigned count = 0; + + bool fail = false; + bool finish = false; + bool starved = false; + + while(!finish) + { + if(tasks.size()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; iwait(); + 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 clean_tgts; + deque 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(b), + options(o), + conf_all(a) +{ + add("architecture", &Loader::architecture); + add("binary_package", &Loader::binpkg); + add("build_type", &Loader::build_type); + add("package", &Loader::package); + + if(!obj.top_loader) + obj.top_loader = this; + else if(!options && obj.top_loader!=this && obj.top_loader->conf_all) + options = obj.top_loader->options; +} + +Builder::Loader::~Loader() +{ + if(obj.top_loader==this) + obj.top_loader = 0; +} + +void Builder::Loader::architecture(const string &n) +{ + if(obj.current_arch->match_name(n)) + load_sub(*obj.current_arch); +} + +void Builder::Loader::binpkg(const string &n) +{ + BinaryPackage *pkg = new BinaryPackage(obj, n); + load_sub(*pkg); +} + +void Builder::Loader::build_type(const string &n) +{ + BuildType btype(n); + load_sub(btype); + auto i = obj.build_types.insert({ n, btype }).first; + if(!obj.build_type) + obj.build_type = &i->second; +} + +void Builder::Loader::package(const string &n) +{ + SourcePackage *pkg = new SourcePackage(obj, n, get_source()); + + load_sub(*pkg, options); + + if(obj.build_type) + pkg->set_build_type(*obj.build_type); +} diff --git a/source/lib/builder.h b/source/lib/builder.h new file mode 100644 index 0000000..e06cec3 --- /dev/null +++ b/source/lib/builder.h @@ -0,0 +1,113 @@ +#ifndef BUILDER_H_ +#define BUILDER_H_ + +#include +#include +#include +#include +#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 + { + 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 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 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 collect_problems() const; + + /** Loads a build file. If opts is not null, it is used to configure any + packages loaded from this file. If all is true, external packages are also + configured. */ + void load_build_file(const Msp::FS::Path &, const Config::InputOptions *opts = 0, bool all = false); + + /** Saves package configuration and dependency caches. */ + void save_caches(); + + /** Builds the goal targets. The build graph must be prepared first. */ + int build(unsigned jobs = 1, bool dry_run = false, bool show_progress = false); + + /** Cleans buildable targets. If all is true, cleans all packages. + Otherwise cleans only the default package. */ + int clean(bool all = false, bool dry_run = false); + + int do_create_makefile(); +}; + +#endif diff --git a/source/lib/buildgraph.cpp b/source/lib/buildgraph.cpp new file mode 100644 index 0000000..070f934 --- /dev/null +++ b/source/lib/buildgraph.cpp @@ -0,0 +1,89 @@ +#include "builder.h" +#include "buildgraph.h" +#include "component.h" +#include "sourcepackage.h" +#include "tool.h" +#include "virtualtarget.h" + +using namespace std; + +BuildGraph::BuildGraph(Builder &b): + builder(b), + goals(new VirtualTarget(builder, "goals")) +{ + Target *world = new VirtualTarget(builder, "world"); + world->add_dependency(*new VirtualTarget(builder, "default")); + world->add_dependency(*new VirtualTarget(builder, "install")); + world->add_dependency(*new VirtualTarget(builder, "archives")); +} + +BuildGraph::~BuildGraph() +{ + for(const auto &kvp: targets) + delete kvp.second; +} + +Target *BuildGraph::get_target(const string &n) const +{ + auto i = targets.find(n); + if(i!=targets.end()) + return i->second; + return 0; +} + +void BuildGraph::add_target(Target *t) +{ + targets.insert({ t->get_name(), t }); +} + +void BuildGraph::add_primary_target(Target &t) +{ + get_target("world")->add_dependency(t); + + Package *main_pkg = &builder.get_package_manager().get_main_package(); + if(t.get_package()==main_pkg && t.get_component() && t.get_component()->is_default()) + get_target("default")->add_dependency(t); +} + +void BuildGraph::add_installed_target(Target &t) +{ + Target *inst_tgt = 0; + if(const Tool *tool = t.get_tool()) + inst_tgt = tool->create_install(t); + if(!inst_tgt) + inst_tgt = builder.get_toolchain().get_tool("CP").create_target(t); + get_target("install")->add_dependency(*inst_tgt); +} + +void BuildGraph::add_goal(Target &t) +{ + goals->add_dependency(t); +} + +void BuildGraph::prepare() +{ + if(goals->get_dependencies().empty()) + add_goal(*get_target("default")); + goals->prepare(); +} + +void BuildGraph::force_full_rebuild() +{ + for(const auto &kvp: targets) + if(kvp.second->is_buildable() && !kvp.second->needs_rebuild()) + kvp.second->force_rebuild(); +} + +unsigned BuildGraph::count_rebuild_targets() const +{ + unsigned count = 0; + for(const auto &kvp: targets) + if(kvp.second->is_buildable() && kvp.second->needs_rebuild()) + ++count; + return count; +} + +Target *BuildGraph::get_buildable_target() const +{ + return goals->get_buildable_target(); +} diff --git a/source/lib/buildgraph.h b/source/lib/buildgraph.h new file mode 100644 index 0000000..371b544 --- /dev/null +++ b/source/lib/buildgraph.h @@ -0,0 +1,66 @@ +#ifndef BUILDGRAPH_H_ +#define BUILDGRAPH_H_ + +#include +#include + +class Builder; +class Target; + +/** +Manages a graph of targets. +*/ +class BuildGraph +{ +private: + Builder &builder; + std::map 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 &get_targets() const { return targets; } + + /** Adds a target. It can later be retrieved by name. Called from Target + constructor. */ + void add_target(Target *); + + /** Adds a target that is a primary build goal. Such targets will be added + as dependencies of the "world" virtual target. If the target belongs to a + default component of the main package, it's also added to the "default" + virtual target. */ + void add_primary_target(Target &); + + /** Adds a target that will be installed. A new InstalledFile target is + created and added as a dependency to the "install" virtual target. */ + void add_installed_target(Target &); + + /** Adds a target as a toplevel goal. These are stored as dependencies of + the "goals" virtual target. */ + void add_goal(Target &); + + Target &get_goals() const { return *goals; } + + /** Prepares all toplevel goals for building. If no goals are defined, the + "default" target is added as a goal. */ + void prepare(); + + /** Marks all buildable targets to be rebuilt. The graph must be prepared + first. */ + void force_full_rebuild(); + + /** Returns the number of targets that are going to be rebuilt. The graph + must be prepared first. */ + unsigned count_rebuild_targets() const; + + /** Returns a target that can be built and is needed for building the goal + targets. Null */ + Target *get_buildable_target() const; +}; + +#endif diff --git a/source/lib/buildinfo.cpp b/source/lib/buildinfo.cpp new file mode 100644 index 0000000..8e11670 --- /dev/null +++ b/source/lib/buildinfo.cpp @@ -0,0 +1,196 @@ +#include +#include +#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 +void unique(vector &v) +{ + vector 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(c)); }); + string::size_type num = i-std.begin(); + type = std.substr(0, num); + year = lexical_cast(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(bi) +{ + add("debug", &BuildInfo::debug); + add("define", &Loader::define); + add("incpath", &Loader::incpath); + add("keep_symbol", &Loader::keep_symbol); + add("libpath", &Loader::libpath); + add("library", &Loader::library); + add("libmode", &BuildInfo::libmode); + add("libmode", &Loader::libmode_for_lib); + add("local_incpath", &Loader::local_incpath); + add("optimize", &BuildInfo::optimize); + add("runtime_path_mode", &BuildInfo::rpath_mode); + add("standard", &Loader::standard); + add("strip", &BuildInfo::strip); + add("sysroot", &Loader::sysroot); + add("threads", &BuildInfo::threads); + add("warning_level", &BuildInfo::warning_level); + add("fatal_warnings", &BuildInfo::fatal_warnings); +} + +void BuildInfo::Loader::incpath(const string &s) +{ + obj.incpath.push_back(s); +} + +void BuildInfo::Loader::define(const string &d, const string &v) +{ + obj.defines[d] = v; +} + +void BuildInfo::Loader::keep_symbol(const string &s) +{ + obj.keep_symbols.push_back(s); +} + +void BuildInfo::Loader::libmode_for_lib(const string &l, LibraryMode m) +{ + obj.libmodes[l] = m; +} + +void BuildInfo::Loader::libpath(const string &s) +{ + obj.libpath.push_back(s); +} + +void BuildInfo::Loader::library(const string &s) +{ + obj.libs.push_back(s); +} + +void BuildInfo::Loader::local_incpath(const string &s) +{ + obj.local_incpath.push_back(s); +} + +void BuildInfo::Loader::standard(DataFile::Symbol tag, const string &std) +{ + obj.standards[tag.name] = std; +} + +void BuildInfo::Loader::sysroot(const string &s) +{ + obj.sysroot = s; +} + + +void operator>>(const LexicalConverter &conv, BuildInfo::LibraryMode &libmode) +{ + if(conv.get()=="FORCE_STATIC") + libmode = BuildInfo::FORCE_STATIC; + else if(conv.get()=="STATIC") + libmode = BuildInfo::STATIC; + else if(conv.get()=="DYNAMIC") + libmode = BuildInfo::DYNAMIC; + else if(conv.get()=="FORCE_DYNAMIC") + libmode = BuildInfo::FORCE_DYNAMIC; + else + throw lexical_error(format("Conversion of '%s' to LibraryMode", conv.get())); +} + + +void operator>>(const LexicalConverter &conv, BuildInfo::RuntimePathMode &rpath_mode) +{ + if(conv.get()=="NONE") + rpath_mode = BuildInfo::NO_RPATH; + else if(conv.get()=="RELATIVE") + rpath_mode = BuildInfo::RELATIVE; + else if(conv.get()=="ABSOLUTE") + rpath_mode = BuildInfo::ABSOLUTE; + else + throw lexical_error(format("Conversion of '%s' to RuntimePathMode", conv.get())); +} diff --git a/source/lib/buildinfo.h b/source/lib/buildinfo.h new file mode 100644 index 0000000..26afabf --- /dev/null +++ b/source/lib/buildinfo.h @@ -0,0 +1,122 @@ +#ifndef BUILDINFO_H_ +#define BUILDINFO_H_ + +#include +#include +#include +#include + +/** +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 + { + 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 + 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 sysroot; + std::map defines; + std::vector incpath; + std::vector local_incpath; + std::vector libpath; + std::vector libs; + Tracked libmode = DYNAMIC; + Tracked rpath_mode = NO_RPATH; + std::map libmodes; + std::vector keep_symbols; + std::map standards; + Tracked threads = false; + Tracked debug = false; + Tracked optimize = 0; + Tracked strip = false; + Tracked warning_level = 0; + Tracked fatal_warnings = false; + + /** Returns the library mode for linking a particular library. If no mode + has been specified for that library, the the global library mode is + returned. */ + LibraryMode get_libmode_for(const std::string &) const; + + /** Updates the BuildInfo from another one. Lists are concatenated, with + the first occurrence of each item preserved. Scalars are overwritten. + + The update level determines what information is updated. */ + void update_from(const BuildInfo &, UpdateLevel = LOCAL); +}; + +#endif diff --git a/source/lib/buildtype.cpp b/source/lib/buildtype.cpp new file mode 100644 index 0000000..2485ca5 --- /dev/null +++ b/source/lib/buildtype.cpp @@ -0,0 +1,15 @@ +#include "buildtype.h" + +using namespace std; +using namespace Msp; + +BuildType::Loader::Loader(BuildType &b): + DataFile::ObjectLoader(b) +{ + add("build_info", &Loader::build_info); +} + +void BuildType::Loader::build_info() +{ + load_sub(obj.build_info); +} diff --git a/source/lib/buildtype.h b/source/lib/buildtype.h new file mode 100644 index 0000000..c16e8f9 --- /dev/null +++ b/source/lib/buildtype.h @@ -0,0 +1,31 @@ +#ifndef BUILDTYPE_H_ +#define BUILDTYPE_H_ + +#include +#include +#include "buildinfo.h" + +class BuildType +{ +public: + class Loader: public Msp::DataFile::ObjectLoader + { + public: + Loader(BuildType &); + + private: + void build_info(); + }; + +private: + std::string name; + BuildInfo build_info; + +public: + BuildType(const std::string &n): name(n) { } + + const std::string &get_name() const { return name; } + const BuildInfo &get_build_info() const { return build_info; } +}; + +#endif diff --git a/source/lib/cache.cpp b/source/lib/cache.cpp new file mode 100644 index 0000000..6baacce --- /dev/null +++ b/source/lib/cache.cpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include +#include +#include "builder.h" +#include "cache.h" +#include "sourcepackage.h" +#include "target.h" + +using namespace std; +using namespace Msp; + +namespace { + +unsigned read_count(IO::Base &in) +{ + unsigned char head = in.get(); + if((head&0xC0)==0xC0) + { + unsigned char tail = in.get(); + return (head&0x3F)<<8 | tail; + } + else + return head; +} + +string read_string(IO::Base &in) +{ + unsigned len = read_count(in); + char buf[0x4000]; + len = in.read(buf, len); + return string(buf, len); +} + +void write_count(IO::Base &out, unsigned count) +{ + if(count<0xC0) + out.put(count); + else if(count<0x4000) + { + out.put(0xC0 | (count>>8)); + out.put(count&0xFF); + } + else + throw invalid_argument("write_count"); +} + +void write_string(IO::Base &out, const string &str) +{ + write_count(out, str.size()); + out.write(str); +} + +} + + +Cache::Cache(SourcePackage &p): + package(p), + filename(package.get_temp_directory()/"../cache") +{ } + +void Cache::set_value(const Target *tgt, const string &k, const string &v) +{ + Values vl; + vl.push_back(v); + set_values(tgt, k, vl); +} + +void Cache::append_value(const Target *tgt, const string &k, const string &v) +{ + Key key(tgt->get_name(), k); + auto i = data.find(key); + if(i==data.end()) + i = data.insert({ key, Values() }).first; + i->second.push_back(v); + changed = true; + package.get_builder().get_logger().log("cache", "Updated key %s %s+ %s", tgt->get_name(), k, v); +} + +void Cache::set_values(const Target *tgt, const string &k, const Values &v) +{ + data[Key(tgt->get_name(), k)] = v; + changed = true; + package.get_builder().get_logger().log("cache", "Updated key %s %s: %s", tgt->get_name(), k, join(v.begin(), v.end())); +} + +const string &Cache::get_value(const Target *tgt, const string &k) +{ + const Values &values = get_values(tgt, k); + if(values.empty()) + throw logic_error("values.empty()"); + return values.front(); +} + +const Cache::Values &Cache::get_values(const Target *tgt, const string &k) +{ + return get_item(data, Key(tgt->get_name(), k)); +} + +bool Cache::has_key(const Target *tgt, const string &k) +{ + return data.count(Key(tgt->get_name(), k)); +} + +void Cache::load() +{ + if(FS::Stat st = FS::stat(filename)) + { + package.get_builder().get_logger().log("files", "Reading %s", filename); + IO::BufferedFile in(filename.str()); + + while(!in.eof()) + { + Key key; + key.first = read_string(in); + key.second = read_string(in); + if(key.first.empty() || key.second.empty()) + break; + Values &values = data[key]; + for(unsigned count = read_count(in); count; --count) + values.push_back(read_string(in)); + package.get_builder().get_logger().log("cache", "Loaded key %s %s: %s", key.first, key.second, join(values.begin(), values.end())); + } + + mtime = st.get_modify_time(); + } +} + +void Cache::save() const +{ + if(data.empty() || !changed) + return; + + FS::Path dir = FS::dirname(filename); + if(!FS::exists(dir)) + FS::mkpath(dir, 0755); + package.get_builder().get_logger().log("files", "Writing %s", filename); + IO::BufferedFile out(filename.str(), IO::M_WRITE); + + for(const auto &kvp: data) + { + write_string(out, kvp.first.first); + write_string(out, kvp.first.second); + write_count(out, kvp.second.size()); + for(const string &v: kvp.second) + write_string(out, v); + } + + changed = false; +} diff --git a/source/lib/cache.h b/source/lib/cache.h new file mode 100644 index 0000000..9193dac --- /dev/null +++ b/source/lib/cache.h @@ -0,0 +1,67 @@ +#ifndef CACHE_H_ +#define CACHE_H_ + +#include +#include +#include +#include + +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; +private: + using Key = std::pair; + + SourcePackage &package; + Msp::FS::Path filename; + std::map data; + Msp::Time::TimeStamp mtime; + mutable bool changed = false; + +public: + Cache(SourcePackage &p); + + /// Sets a key to a single value, replacing any existing values. + void set_value(const Target *, const std::string &, const std::string &); + + /// Appends a value to a key. If the key does not exist, it is created. + void append_value(const Target *, const std::string &, const std::string &); + + /// Sets a key to a list of values, replacing any existing values. + void set_values(const Target *, const std::string &, const Values &); + + /** Returns the first value from a key. The key must exist and be + non-empty. */ + const std::string &get_value(const Target *, const std::string &); + + /// Returns the values from a key. The key must exist. + const Values &get_values(const Target *, const std::string &); + + /// Indicates whether a key exists. + bool has_key(const Target *, const std::string &); + + /// Returns the last modification timestamp of the cache. + const Msp::Time::TimeStamp &get_mtime() const { return mtime; } + + /** Loads the cache file and sets the last modification timestamp + accordingly. */ + void load(); + + /** Saves the cache. Does nothing if there is no data to save or nothing + has changed. */ + void save() const; +}; + +#endif diff --git a/source/lib/chainedtask.cpp b/source/lib/chainedtask.cpp new file mode 100644 index 0000000..a232ef8 --- /dev/null +++ b/source/lib/chainedtask.cpp @@ -0,0 +1,66 @@ +#include +#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(currentcheck())) ; + + return final_status; +} + +Task::Status ChainedTask::wait() +{ + while(currentwait())) ; + + return final_status; +} + +bool ChainedTask::process(Status sub_status) +{ + if(sub_status==SUCCESS && current+1start(); + return true; + } + + if(sub_status!=RUNNING) + { + // The task is not running anymore and either failed or was the last one + current = tasks.size(); + final_status = sub_status; + } + + return false; +} diff --git a/source/lib/chainedtask.h b/source/lib/chainedtask.h new file mode 100644 index 0000000..30a3b3e --- /dev/null +++ b/source/lib/chainedtask.h @@ -0,0 +1,32 @@ +#ifndef CHAINEDTASK_H_ +#define CHAINEDTASK_H_ + +#include +#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 tasks; + unsigned current = 0; + Status final_status = RUNNING; + +public: + ChainedTask(Task *t) { add_task(t); } + ~ChainedTask(); + + void add_task(Task *); + + std::string get_command() const override; + void start() override; + Status check() override; + Status wait() override; +private: + bool process(Status); +}; + +#endif diff --git a/source/lib/component.cpp b/source/lib/component.cpp new file mode 100644 index 0000000..4fddcef --- /dev/null +++ b/source/lib/component.cpp @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include +#include +#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 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 Component::collect_source_files() const +{ + vector files; + for(const FS::Path &p: sources) + { + if(FS::is_dir(p)) + { + vector 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 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(c), + ConditionalLoader(c.package, format("%s/%s", c.package.get_name(), c.name)) +{ + add("overlay", &Loader::overlay); + add("source", &Loader::source); + add("install", &Component::install); + add("install_map", &Loader::install_map); + add("build_info", &Loader::build_info); + add("require", &Loader::require); + add("default", &Component::deflt); +} + +void Component::Loader::build_info() +{ + load_sub(obj.build_info); +} + +void Component::Loader::install_map() +{ + load_sub(obj.install_map, obj.package.get_source_directory()); +} + +void Component::Loader::overlay(const string &o) +{ + obj.overlays.push_back(o); +} + +void Component::Loader::require(const string &n) +{ + Package *req = obj.package.get_builder().get_package_manager().find_package(n); + if(req) + obj.requires.push_back(req); + else + obj.problems.push_back(format("Required package %s not found", n)); +} + +void Component::Loader::source(const string &s) +{ + obj.sources.push_back((obj.package.get_source_directory()/s).str()); +} diff --git a/source/lib/component.h b/source/lib/component.h new file mode 100644 index 0000000..7ddd972 --- /dev/null +++ b/source/lib/component.h @@ -0,0 +1,87 @@ +#ifndef COMPONENT_H_ +#define COMPONENT_H_ + +#include +#include +#include +#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, 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 sources; + std::vector overlays; + bool install = false; + BuildInfo build_info; + Package::Requirements requires; + bool deflt = true; + InstallMap install_map; + std::vector 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 &get_sources() const { return sources; } + + const std::vector &get_overlays() const { return overlays; } + +protected: + /** Returns a list of all source files for the component. */ + std::vector 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 &get_problems() const { return problems; } + + /** Prepares any required packages. */ + void prepare(); + + /** Prepares the build information for building. Pulls build info from the + parent and dependency packages, and adds any component-specific flags. */ + virtual void create_build_info(); + + virtual void update_exported_build_info(BuildInfo &) const { } + + const BuildInfo &get_build_info() const { return build_info; } + + BuildInfo get_build_info_for_path(const Msp::FS::Path &) const; + + virtual void create_targets() const = 0; +}; + +#endif diff --git a/source/lib/conditionalloader.cpp b/source/lib/conditionalloader.cpp new file mode 100644 index 0000000..b9d9d25 --- /dev/null +++ b/source/lib/conditionalloader.cpp @@ -0,0 +1,47 @@ +#include "booleanevaluator.h" +#include "builder.h" +#include "conditionalloader.h" +#include "sourcepackage.h" + +using namespace std; +using namespace Msp; + +ArchitectureConditional::ArchitectureConditional(const Builder &b, const string &l): + builder(b), + log_prefix(l) +{ + add("if_arch", &ArchitectureConditional::if_arch); +} + +void ArchitectureConditional::if_arch(const string &cond) +{ + const Architecture &arch = builder.get_current_arch(); + BooleanEvaluator eval([&arch](const string &value){ return arch.match_name(value); }); + bool match = eval.evaluate(cond); + builder.get_logger().log("configure", "%s: arch %s %smatched", log_prefix, cond, (match ? "" : "not ")); + if(match) + load_sub_with(*this); +} + + +FeatureConditional::FeatureConditional(const SourcePackage &p, const string &l): + package(p), + log_prefix(l) +{ + add("if_feature", &FeatureConditional::if_feature); +} + +void FeatureConditional::if_feature(const string &cond) +{ + BooleanEvaluator eval([this](const string &feat, const string *value){ return package.match_feature(feat, value); }); + bool match = eval.evaluate(cond); + package.get_builder().get_logger().log("configure", "%s: feature %s %smatched", log_prefix, cond, (match ? "" : "not ")); + if(match) + load_sub_with(*this); +} + + +ConditionalLoader::ConditionalLoader(const SourcePackage &p, const string &l): + ArchitectureConditional(p.get_builder(), l), + FeatureConditional(p, l) +{ } diff --git a/source/lib/conditionalloader.h b/source/lib/conditionalloader.h new file mode 100644 index 0000000..5184fac --- /dev/null +++ b/source/lib/conditionalloader.h @@ -0,0 +1,43 @@ +#ifndef CONDITIONALLOADER_H_ +#define CONDITIONALLOADER_H_ + +#include +#include + +class Builder; +class SourcePackage; + +class ArchitectureConditional: virtual public Msp::DataFile::Loader +{ +private: + const Builder &builder; + std::string log_prefix; + +protected: + ArchitectureConditional(const Builder &, const std::string &); + +private: + void if_arch(const std::string &); +}; + + +class FeatureConditional: virtual public Msp::DataFile::Loader +{ +private: + const SourcePackage &package; + std::string log_prefix; + +protected: + FeatureConditional(const SourcePackage &, const std::string &); + + void if_feature(const std::string &); +}; + + +class ConditionalLoader: public ArchitectureConditional, FeatureConditional +{ +protected: + ConditionalLoader(const SourcePackage &, const std::string &); +}; + +#endif diff --git a/source/lib/config.cpp b/source/lib/config.cpp new file mode 100644 index 0000000..1d3b180 --- /dev/null +++ b/source/lib/config.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include +#include +#include +#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(c) +{ + add("option", &Loader::option); +} + +void Config::Loader::option(const string &n, const string &v) +{ + if(obj.options.count(n)) + obj.set_option(n, v); + else + obj.pending_options[n] = v; +} diff --git a/source/lib/config.h b/source/lib/config.h new file mode 100644 index 0000000..ba8416b --- /dev/null +++ b/source/lib/config.h @@ -0,0 +1,66 @@ +#ifndef CONFIG_H_ +#define CONFIG_H_ + +#include +#include +#include +#include +#include +#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; + +private: + class Loader: public Msp::DataFile::ObjectLoader + { + public: + Loader(Config &); + private: + void option(const std::string &, const std::string &); + }; + + SourcePackage &package; + std::map 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 &get_options() const { return options; } + const Msp::Time::TimeStamp &get_mtime() const { return mtime; } + + void load(); + void save() const; +}; + +#endif diff --git a/source/lib/csourcefile.cpp b/source/lib/csourcefile.cpp new file mode 100644 index 0000000..bc76605 --- /dev/null +++ b/source/lib/csourcefile.cpp @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#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(mtimeget_build_info_for_path(path); + const auto &incpath = build_info.incpath; + VirtualFileSystem::SearchPath local_incpath; + local_incpath.reserve(1+build_info.local_incpath.size()+incpath.size()); + local_incpath.push_back(FS::dirname(path).str()); + local_incpath.insert(local_incpath.end(), build_info.local_incpath.begin(), build_info.local_incpath.end()); + local_incpath.insert(local_incpath.end(), incpath.begin(), incpath.end()); + + Tool *compiler = builder.get_toolchain().get_tool_for_suffix(FS::extpart(FS::basename(path)), true); + if(compiler) + compiler->prepare(); + for(const string &i: includes) + if(Target *hdr = builder.get_vfs().find_header(i.substr(1), compiler, (i[0]=='"' ? local_incpath : incpath))) + add_transitive_dependency(*hdr); +} + +void CSourceFile::modified() +{ + includes.clear(); + trans_depends.clear(); + find_dependencies(); +} diff --git a/source/lib/csourcefile.h b/source/lib/csourcefile.h new file mode 100644 index 0000000..c57654a --- /dev/null +++ b/source/lib/csourcefile.h @@ -0,0 +1,27 @@ +#ifndef CSOURCEFILE_H_ +#define CSOURCEFILE_H_ + +#include +#include "sourcefile.h" + +/** +Represents a C or C++ source file. +*/ +class CSourceFile: public SourceFile +{ +protected: + std::vector 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 &get_includes() const { return includes; } +protected: + virtual void parse_includes(Msp::IO::Base &); + void find_dependencies() override; + void modified() override; +}; + +#endif diff --git a/source/lib/customizedtool.cpp b/source/lib/customizedtool.cpp new file mode 100644 index 0000000..9171996 --- /dev/null +++ b/source/lib/customizedtool.cpp @@ -0,0 +1,59 @@ +#include "builder.h" +#include "customizedtool.h" +#include "target.h" + +using namespace std; +using namespace Msp; + +CustomizedTool::CustomizedTool(Builder &b, const std::string &t, const Architecture &a): + CustomizedTool(b.get_toolchain().get_tool(t), a) +{ } + +CustomizedTool::CustomizedTool(Tool &t, const Architecture &a): + Tool(t.get_builder(), &a, t.get_tag()), + parent(t) +{ + input_suffixes = parent.get_input_suffixes(); + aux_suffixes = parent.get_auxiliary_suffixes(); + processing_unit = parent.get_processing_unit(); + + set_run([this](const Target &tgt){ return parent.run(tgt); }); +} + +Target *CustomizedTool::create_source(const Component &c, const FS::Path &p) const +{ + return parent.create_source(c, p); +} + +Target *CustomizedTool::create_source(const FS::Path &p) const +{ + return parent.create_source(p); +} + +Target *CustomizedTool::create_target(const vector &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)); +} diff --git a/source/lib/customizedtool.h b/source/lib/customizedtool.h new file mode 100644 index 0000000..55c4c6a --- /dev/null +++ b/source/lib/customizedtool.h @@ -0,0 +1,25 @@ +#ifndef CUSTOMIZEDTOOL_H_ +#define CUSTOMIZEDTOOL_H_ + +#include "tool.h" + +class CustomizedTool: public Tool +{ +protected: + Tool &parent; + + CustomizedTool(Builder &, const std::string &, const Architecture &); + CustomizedTool(Tool &, const Architecture &); + +public: + const Tool *get_base_tool() const override { return &parent; } + Target *create_source(const Component &, const Msp::FS::Path &) const override; + Target *create_source(const Msp::FS::Path &) const override; + Target *create_target(const std::vector &, const std::string & = std::string()) override; + Target *create_install(Target &) const override; + std::string create_build_signature(const BuildInfo &) const override; +protected: + void do_prepare(ToolData &) const override; +}; + +#endif diff --git a/source/lib/executable.cpp b/source/lib/executable.cpp new file mode 100644 index 0000000..aa0d8fb --- /dev/null +++ b/source/lib/executable.cpp @@ -0,0 +1,13 @@ +#include "builder.h" +#include "component.h" +#include "executable.h" +#include "sourcepackage.h" + +using namespace std; +using namespace Msp; + +Executable::Executable(Builder &b, const Component &c, const vector &objs): + Binary(b, c, b.get_current_arch().create_filename(c.get_name()), objs) +{ + install_location = "bin"; +} diff --git a/source/lib/executable.h b/source/lib/executable.h new file mode 100644 index 0000000..5728e2f --- /dev/null +++ b/source/lib/executable.h @@ -0,0 +1,15 @@ +#ifndef EXECUTABLE_H_ +#define EXECUTABLE_H_ + +#include "binary.h" + +class Executable: public Binary +{ +public: + Executable(Builder &b, const Msp::FS::Path &p): Binary(b, p) { } + Executable(Builder &, const Component &, const std::vector &); + + const char *get_type() const override { return "Executable"; } +}; + +#endif diff --git a/source/lib/exportdefinitions.cpp b/source/lib/exportdefinitions.cpp new file mode 100644 index 0000000..7edf759 --- /dev/null +++ b/source/lib/exportdefinitions.cpp @@ -0,0 +1,20 @@ +#include "component.h" +#include "exportdefinitions.h" +#include "objectfile.h" +#include "sourcepackage.h" + +using namespace std; +using namespace Msp; + +ExportDefinitions::ExportDefinitions(Builder &b, const Component &c, const vector &objs): + FileTarget(b, c.get_package(), generate_target_path(c)) +{ + component = &c; + for(ObjectFile *o: objs) + add_dependency(*o); +} + +FS::Path ExportDefinitions::generate_target_path(const Component &comp) +{ + return comp.get_package().get_temp_directory()/comp.get_name()/(comp.get_name()+".def"); +} diff --git a/source/lib/exportdefinitions.h b/source/lib/exportdefinitions.h new file mode 100644 index 0000000..b8631ee --- /dev/null +++ b/source/lib/exportdefinitions.h @@ -0,0 +1,22 @@ +#ifndef EXPORTDEFINITIONS_H_ +#define EXPORTDEFINITIONS_H_ + +#include "filetarget.h" + +class ObjectFile; + +/** +An export definition file for a shared library. Only used on Windows. +*/ +class ExportDefinitions: public FileTarget +{ +public: + ExportDefinitions(Builder &, const Component &, const std::vector &); +private: + static Msp::FS::Path generate_target_path(const Component &); + +public: + const char *get_type() const override { return "ExportDefinitions"; } +}; + +#endif diff --git a/source/lib/externaltask.cpp b/source/lib/externaltask.cpp new file mode 100644 index 0000000..37a36d3 --- /dev/null +++ b/source/lib/externaltask.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include "externaltask.h" + +using namespace std; +using namespace Msp; + +ExternalTask::ExternalTask(const Arguments &a, const FS::Path &wd): + argv(a), + work_dir(wd) +{ + if(argv.empty()) + throw invalid_argument("ExternalTask::ExternalTask"); +} + +ExternalTask::~ExternalTask() +{ + delete capture_pipe; +} + +string ExternalTask::get_command() const +{ + string cmd; + for(const string &a: argv) + { + if(!cmd.empty()) + cmd += ' '; + + for(char c: a) + { + if(c=='"' || c=='\'' || c==' ' || c=='\\' || c=='&') + cmd += '\\'; + cmd += c; + } + } + + if(stdin_action==REDIRECT) + { + cmd += " <"; + cmd += stdin_file.str(); + } + + if(stdout_action==REDIRECT) + { + cmd += " >"; + cmd += stdout_file.str(); + } + + return cmd; +} + +void ExternalTask::start() +{ + IO::File *devnull = 0; + IO::File *infile = 0; + IO::File *outfile = 0; + + prepare(); + + process = new Process; + + if(stdin_action==IGNORE || stdout_action==IGNORE || stderr_action==IGNORE) + { +#ifdef _WIN32 + devnull = new IO::File("nul", IO::M_RDWR); +#else + devnull = new IO::File("/dev/null", IO::M_RDWR); +#endif + if(stdin_action==IGNORE) + process->redirect_cin(*devnull); + if(stdout_action==IGNORE) + process->redirect_cout(*devnull); + if(stderr_action==IGNORE) + process->redirect_cerr(*devnull); + } + + if(stdout_action==REDIRECT) + { + outfile = new IO::File((work_dir/stdout_file).str(), IO::M_WRITE); + process->redirect_cout(*outfile); + } + + if(stdout_action==CAPTURE || stderr_action==CAPTURE) + { + capture_pipe = new IO::Pipe; + if(stdout_action==CAPTURE) + process->redirect_cout(*capture_pipe); + if(stderr_action==CAPTURE) + process->redirect_cerr(*capture_pipe); + } + + if(stdin_action==REDIRECT) + { + infile = new IO::File((work_dir/stdin_file).str()); + process->redirect_cin(*infile); + } + + if(!work_dir.empty()) + process->set_working_directory(work_dir); + + Process::Arguments args(argv.begin()+1, argv.end()); + process->execute(argv.front(), args); + if(capture_pipe) + capture_pipe->set_mode(IO::M_READ); + + delete devnull; + delete infile; + delete outfile; +} + +Task::Status ExternalTask::check() +{ + return do_wait(false); +} + +Task::Status ExternalTask::wait() +{ + return do_wait(true); +} + +Task::Status ExternalTask::do_wait(bool block) +{ + while(process) + { + if(process->wait(block && !capture_pipe)) + { + exit_code = process->get_exit_code(); + delete process; + process = 0; + } + + // Do this after waiting to avoid a race condition + while(capture_pipe && IO::poll(*capture_pipe, IO::P_INPUT, 10*Time::msec)) + { + char buf[1024]; + unsigned len = capture_pipe->read(buf, sizeof(buf)); + if(len) + output.append(buf, len); + else + break; + } + + if(process) + { + if(!block) + return RUNNING; + } + else + signal_finished.emit(!exit_code); + } + + return exit_code ? ERROR : SUCCESS; +} + +void ExternalTask::set_stdin(const FS::Path &f) +{ + stdin_action = REDIRECT; + stdin_file = f; +} + +void ExternalTask::set_stdout(StreamAction a) +{ + if(a==REDIRECT) + throw invalid_argument("ExternalTask::set_stdout"); + stdout_action = a; +} + +void ExternalTask::set_stdout(const FS::Path &f) +{ + stdout_action = REDIRECT; + stdout_file = f; +} + +void ExternalTask::set_stderr(StreamAction a) +{ + if(a==REDIRECT) + throw invalid_argument("ExternalTask::set_stdout"); + stderr_action = a; +} + +string ExternalTask::run_and_capture_output(const Arguments &argv, const FS::Path &wd, bool capture_stderr) +{ + ExternalTask task(argv, wd); + task.stdin_action = IGNORE; + task.set_stdout(CAPTURE); + task.set_stderr(capture_stderr ? CAPTURE : IGNORE); + task.start(); + if(task.wait()!=SUCCESS) + throw runtime_error(format("%s failed", argv.front())); + return task.get_output(); +} diff --git a/source/lib/externaltask.h b/source/lib/externaltask.h new file mode 100644 index 0000000..9b9bc43 --- /dev/null +++ b/source/lib/externaltask.h @@ -0,0 +1,79 @@ +#ifndef EXTERNALTASK_H_ +#define EXTERNALTASK_H_ + +#include +#include +#include +#include +#include +#include "task.h" + +/** +Runs an external command. A zero exit status is translated to a SUCCESS status +for the task, and anything else is treated as an error. Output can optionally +be captured. +*/ +class ExternalTask: public Task +{ +public: + enum StreamAction + { + PASSTHROUGH, //< Do not touch the stream + CAPTURE, //< Capture the stream + REDIRECT, //< Redirect the stream to/from a file + IGNORE //< Redirect the stream to oblivion + }; + + using Arguments = Msp::Process::Arguments; + +private: + Arguments argv; + Msp::FS::Path work_dir; + Msp::Process *process = 0; + int exit_code = -1; + StreamAction stdin_action = PASSTHROUGH; + Msp::FS::Path stdin_file; + StreamAction stdout_action = PASSTHROUGH; + Msp::FS::Path stdout_file; + StreamAction stderr_action = PASSTHROUGH; + Msp::IO::Pipe *capture_pipe = 0; + std::string output; + +public: + /** Creates an ExternalTask with an argument array and an optional working + directory. The first element of the argument array should be the command + name. If the working directory is not specified, no chdir is done. */ + ExternalTask(const Arguments &, const Msp::FS::Path & = Msp::FS::Path()); + + ~ExternalTask(); + + std::string get_command() const override; + void start() override; + Status check() override; + Status wait() override; +private: + Status do_wait(bool); + +public: + /// Redirect stdin from a file. Has no effect after the task is started. + void set_stdin(const Msp::FS::Path &); + + /// Sets destination for stdout. Has no effect after the task is started. + void set_stdout(StreamAction); + + /// Redirect stdout to a file. Has no effect after the task is started. + void set_stdout(const Msp::FS::Path &); + + /// Sets destination for stderr. Has no effect after the task is started. + void set_stderr(StreamAction); + + /** Returns captured output, if any. This may be called while the task is + still running, but it will always return all output. */ + const std::string &get_output() const { return output; } + + /** Executes a command and captures its output. If the command exits with + a nonzero status, an exception is thrown. */ + static std::string run_and_capture_output(const Arguments &, const Msp::FS::Path & = Msp::FS::Path(), bool = false); +}; + +#endif diff --git a/source/lib/feature.cpp b/source/lib/feature.cpp new file mode 100644 index 0000000..2518377 --- /dev/null +++ b/source/lib/feature.cpp @@ -0,0 +1,20 @@ +#include "feature.h" + +using namespace std; +using namespace Msp; + +Feature::Loader::Loader(Feature &f): + Msp::DataFile::ObjectLoader(f) +{ + add("choice", &Loader::choice); + add("description", &Feature::description); + add("default", &Feature::default_value); + add("export", &Feature::exported); +} + +void Feature::Loader::choice(const string &c) +{ + if(obj.choices.empty()) + obj.default_value = c; + obj.choices.push_back(c); +} diff --git a/source/lib/feature.h b/source/lib/feature.h new file mode 100644 index 0000000..0dca1f5 --- /dev/null +++ b/source/lib/feature.h @@ -0,0 +1,26 @@ +#ifndef FEATURE_H_ +#define FEATURE_H_ + +#include + +struct Feature +{ + class Loader: public Msp::DataFile::ObjectLoader + { + public: + Loader(Feature &); + + private: + void choice(const std::string &); + }; + + std::string name; + std::string description; + std::string default_value = "no"; + std::vector choices; + bool exported = false; + + Feature(const std::string &n): name(n) { } +}; + +#endif diff --git a/source/lib/file.h b/source/lib/file.h new file mode 100644 index 0000000..42d73b8 --- /dev/null +++ b/source/lib/file.h @@ -0,0 +1,18 @@ +#ifndef FILE_H_ +#define FILE_H_ + +#include "filetarget.h" + +/** +Just an arbitrary file. No special meaning attached. +*/ +class File: public FileTarget +{ +public: + File(Builder &b, const Msp::FS::Path &t): FileTarget(b, t) { } + File(Builder &b, const SourcePackage &p, const Msp::FS::Path &t): FileTarget(b, p, t) { } + + const char *get_type() const override { return "File"; } +}; + +#endif diff --git a/source/lib/filetarget.cpp b/source/lib/filetarget.cpp new file mode 100644 index 0000000..a2bd3f0 --- /dev/null +++ b/source/lib/filetarget.cpp @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include +#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 ""+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(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 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 seen_tools; + vector tool_sigs; + for(Target *d: depends) + if(const Tool *t = d->get_tool()) + if(d->get_component()==component && !any_equals(seen_tools, t)) + { + seen_tools.push_back(t); + tool_sigs.push_back(t->create_build_signature(binfo)); + } + + sort(tool_sigs); + sigs.insert(sigs.end(), make_move_iterator(tool_sigs.begin()), make_move_iterator(tool_sigs.end())); + } + + return join(sigs.begin(), sigs.end(), ";"); +} + +void FileTarget::build(Task &task) +{ + task.add_file(path); + task.set_unlink(true); +} + +void FileTarget::build_finished(bool success) +{ + if(success) + { + stat(); + if(package) + { + string build_sig = create_build_signature(); + if(!build_sig.empty()) + package->get_cache().set_value(this, "build_sig", build_sig); + } + } + + Target::build_finished(success); +} + +void FileTarget::clean() +{ + if(mtime) + { + FS::unlink(path); + mtime = Time::TimeStamp(); + size = 0; + check_rebuild(); + } +} diff --git a/source/lib/filetarget.h b/source/lib/filetarget.h new file mode 100644 index 0000000..da165d0 --- /dev/null +++ b/source/lib/filetarget.h @@ -0,0 +1,54 @@ +#ifndef FILETARGET_H_ +#define FILETARGET_H_ + +#include +#include "target.h" + +/** +An intermediate base class for targets that represent files. Almost all target +classes are derived from this. +*/ +class FileTarget: public Target +{ +protected: + Msp::FS::Path path; + Msp::Time::TimeStamp mtime; + unsigned size = 0; + Msp::FS::Path install_location; + std::string install_filename; + bool nested_build_sig = false; + bool arch_in_build_sig = false; + + FileTarget(Builder &b, const Msp::FS::Path &a): FileTarget(b, 0, a) { } + FileTarget(Builder &b, const SourcePackage &p, const Msp::FS::Path &a): FileTarget(b, &p, a) { } +private: + FileTarget(Builder &, const SourcePackage *, const Msp::FS::Path &); + void stat(); + static std::string generate_name(Builder &, const SourcePackage *, const Msp::FS::Path &); + +public: + const Msp::FS::Path &get_path() const { return path; } + const Msp::Time::TimeStamp &get_mtime() const { return mtime; } + unsigned get_size() const { return size; } + + bool is_installable() const { return !install_location.empty(); } + const Msp::FS::Path &get_install_location() const { return install_location; } + const std::string &get_install_filename() const { return install_filename; } + + /// Changes the mtime of the target to the current time. + void touch(); + +protected: + void check_rebuild() override; + + virtual std::string create_build_signature() const; + + void build(Task &) override; + + void build_finished(bool) override; + +public: + void clean() override; +}; + +#endif diff --git a/source/lib/importlibrary.cpp b/source/lib/importlibrary.cpp new file mode 100644 index 0000000..435fb30 --- /dev/null +++ b/source/lib/importlibrary.cpp @@ -0,0 +1,35 @@ +#include +#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(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(sl.get_libname()); +} diff --git a/source/lib/importlibrary.h b/source/lib/importlibrary.h new file mode 100644 index 0000000..1c5d682 --- /dev/null +++ b/source/lib/importlibrary.h @@ -0,0 +1,30 @@ +#ifndef IMPORTLIBRARY_H_ +#define IMPORTLIBRARY_H_ + +#include "filetarget.h" + +class ExportDefinitions; +class SharedLibrary; + +/** +A special case of static library which pulls in a shared library. Used on +platforms with no true dynamic linking support. +*/ +class ImportLibrary: public FileTarget +{ +private: + SharedLibrary *shared_lib = 0; + +public: + ImportLibrary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { } + ImportLibrary(Builder &, const Component &, SharedLibrary &, ExportDefinitions &); +private: + static std::string generate_filename(const Component &, const SharedLibrary &); + +public: + const char *get_type() const override { return "ImportLibrary"; } + + SharedLibrary *get_shared_library() const { return shared_lib; } +}; + +#endif diff --git a/source/lib/installcomponent.cpp b/source/lib/installcomponent.cpp new file mode 100644 index 0000000..86919fa --- /dev/null +++ b/source/lib/installcomponent.cpp @@ -0,0 +1,23 @@ +#include "installcomponent.h" +#include "builder.h" +#include "file.h" +#include "sourcepackage.h" +#include "tool.h" + +using namespace std; +using namespace Msp; + +void InstallComponent::create_targets() const +{ + Builder &builder = package.get_builder(); + Target *inst = builder.get_build_graph().get_target("install"); + Tool © = builder.get_toolchain().get_tool("CP"); + + for(const FS::Path &s: collect_source_files()) + { + Target *tgt = builder.get_vfs().get_target(s); + if(!tgt) + tgt = new File(builder, package, s); + inst->add_dependency(*copy.create_target(*tgt, name)); + } +} diff --git a/source/lib/installcomponent.h b/source/lib/installcomponent.h new file mode 100644 index 0000000..457256d --- /dev/null +++ b/source/lib/installcomponent.h @@ -0,0 +1,14 @@ +#ifndef INSTALLCOMPONENT_H_ +#define INSTALLCOMPONENT_H_ + +#include "component.h" + +class InstallComponent: public Component +{ +public: + InstallComponent(SourcePackage &p, const std::string &n): Component(p, n) { } + + void create_targets() const override; +}; + +#endif diff --git a/source/lib/installedfile.cpp b/source/lib/installedfile.cpp new file mode 100644 index 0000000..6e45ad2 --- /dev/null +++ b/source/lib/installedfile.cpp @@ -0,0 +1,92 @@ +#include +#include +#include "builder.h" +#include "installedfile.h" +#include "sharedlibrary.h" + +using namespace std; +using namespace Msp; + +InstalledFile::InstalledFile(Builder &b, const SourcePackage &p, FileTarget &s, const string &loc): + FileTarget(b, p, generate_target_path(b.get_prefix(), s, loc)), + source(s) +{ + add_dependency(source); +} + +FS::Path InstalledFile::generate_target_path(const FS::Path &global_prefix, const FileTarget &tgt, const string &loc) +{ + if(!tgt.is_installable() && loc.empty()) + throw invalid_argument(tgt.get_name()+" is not installable"); + + FS::Path prefix; + FS::Path mid; + if(!loc.compare(0, 2, "//")) + { + if(!tgt.get_package()) + throw invalid_argument("No private install location for "+tgt.get_name()); + + prefix = tgt.get_package()->get_temp_directory(); + mid = loc.substr(2); + } + else + { + prefix = global_prefix; + + if(!loc.empty()) + mid = loc; + else if(const Component *comp = tgt.get_component()) + mid = comp->get_install_map().get_install_location(tgt); + } + + if(mid.empty()) + mid = tgt.get_install_location(); + + string fn = tgt.get_install_filename(); + if(fn.empty()) + fn = FS::basename(tgt.get_path()); + + return prefix/mid/fn; +} + +void InstalledFile::set_symlink(const FS::Path &l) +{ + FS::Path al = FS::dirname(path)/l; + if(al==path) + throw invalid_argument("InstalledFile::set_symlink"); + link = FS::dirname(path)/l; + builder.get_vfs().register_path(link, this); +} + +Target *InstalledFile::get_real_target() +{ + return source.get_real_target(); +} + +void InstalledFile::check_rebuild() +{ + if(!mtime) + mark_rebuild("Does not exist"); + else if(source.get_mtime()>mtime || source.get_size()!=size) + mark_rebuild(source.get_name()+" has changed"); + else if(source.needs_rebuild()) + mark_rebuild(source.get_name()+" needs rebuilding"); + if(!needs_rebuild() && !link.empty()) + { + if(!FS::exists(link)) + mark_rebuild("Symlink does not exist"); + else + { + FS::Path rel_path = FS::relative(path, FS::dirname(link)); + if(FS::readlink(link)!=rel_path) + mark_rebuild("Symlink needs updating"); + } + } +} + +void InstalledFile::clean() +{ + if(!link.empty() && mtime) + FS::unlink(link); + FileTarget::clean(); +} diff --git a/source/lib/installedfile.h b/source/lib/installedfile.h new file mode 100644 index 0000000..0dd671f --- /dev/null +++ b/source/lib/installedfile.h @@ -0,0 +1,39 @@ +#ifndef INSTALLEDFILE_H_ +#define INSTALLEDFILE_H_ + +#include "sourcepackage.h" +#include "filetarget.h" + +/** +Represents the installation of a file. +*/ +class InstalledFile: public FileTarget +{ +private: + FileTarget &source; + Msp::FS::Path link; + +public: + InstalledFile(Builder &, const SourcePackage &, FileTarget &, const std::string & =std::string()); +private: + static Msp::FS::Path generate_target_path(const Msp::FS::Path &, const FileTarget &i, const std::string &); + +public: + const char *get_type() const override { return "InstalledFile"; } + FileTarget &get_source() const { return source; } + + /** Sets a symlink for the file. A relative path will be rooted at the + directory the file resides in. */ + void set_symlink(const Msp::FS::Path &); + + const Msp::FS::Path &get_symlink() const { return link; } + + Target *get_real_target() override; +private: + void check_rebuild() override; + +public: + void clean() override; +}; + +#endif diff --git a/source/lib/installmap.cpp b/source/lib/installmap.cpp new file mode 100644 index 0000000..448f997 --- /dev/null +++ b/source/lib/installmap.cpp @@ -0,0 +1,82 @@ +#include +#include +#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(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(m), + source_base(s) +{ + add("map", &Loader::map); +} + +void InstallMap::Loader::map(const string &src, const string &inst) +{ + obj.add_mapping(source_base/src, inst); +} diff --git a/source/lib/installmap.h b/source/lib/installmap.h new file mode 100644 index 0000000..49211d7 --- /dev/null +++ b/source/lib/installmap.h @@ -0,0 +1,52 @@ +#ifndef INSTALLMAP_H_ +#define INSTALLMAP_H_ + +#include +#include +#include + +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 + { + 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 entries; + +public: + /** Adds an install mapping. Multiple mappings can be specified for the + same source location, but the first one that matches both that and the + target's default install location will be used. */ + void add_mapping(const Msp::FS::Path &, const Msp::FS::Path &); + + /** Returns the install location for a target. If no defined mappings match + the target, its default install location is returned. */ + Msp::FS::Path get_install_location(const FileTarget &) const; +}; + +#endif diff --git a/source/lib/internaltask.cpp b/source/lib/internaltask.cpp new file mode 100644 index 0000000..fc929ce --- /dev/null +++ b/source/lib/internaltask.cpp @@ -0,0 +1,36 @@ +#include "internaltask.h" + +InternalTask::~InternalTask() +{ + worker.join(); +} + +void InternalTask::start() +{ + prepare(); + worker.launch(); +} + +Task::Status InternalTask::check() +{ + Status result = worker.get_status(); + if(result!=RUNNING) + signal_finished.emit(result==SUCCESS); + return result; +} + +Task::Status InternalTask::wait() +{ + Status result; + while((result = check())==RUNNING) ; + return result; +} + + +void InternalTask::Worker::main() +{ + if(func()) + status = Task::SUCCESS; + else + status = Task::ERROR; +} diff --git a/source/lib/internaltask.h b/source/lib/internaltask.h new file mode 100644 index 0000000..608ea96 --- /dev/null +++ b/source/lib/internaltask.h @@ -0,0 +1,45 @@ +#ifndef INTERNALTASK_H_ +#define INTERNALTASK_H_ + +#include +#include +#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 func; + volatile Status status = Task::RUNNING; + + public: + Worker(std::function f): func(f) { } + + Status get_status() const { return status; } + + private: + void main() override; + }; + + Worker worker; + +public: + InternalTask(std::function f): worker(f) { } + ~InternalTask(); + + std::string get_command() const override { return ""; } + void start() override; + Status check() override; + Status wait() override; +}; + +#endif diff --git a/source/lib/logger.cpp b/source/lib/logger.cpp new file mode 100644 index 0000000..35d4507 --- /dev/null +++ b/source/lib/logger.cpp @@ -0,0 +1,37 @@ +#include +#include +#include "logger.h" + +using namespace std; +using namespace Msp; + +void Logger::enable_channel(const string &chan) +{ + auto i = lower_bound(enabled_channels, chan); + if(i==enabled_channels.end() || *i!=chan) + enabled_channels.insert(i, chan); +} + +void Logger::disable_channel(const string &chan) +{ + auto i = lower_bound(enabled_channels, chan); + if(i!=enabled_channels.end() && *i==chan) + enabled_channels.erase(i); +} + +bool Logger::is_channel_enabled(const string &chan) const +{ + auto i = lower_bound(enabled_channels, chan); + return (i!=enabled_channels.end() && *i==chan); +} + +void Logger::log(const string &chan, const string &message) const +{ + if(is_channel_enabled(chan)) + print(message); +} + +void Logger::print(const string &message) const +{ + IO::print("%s\n", message); +} diff --git a/source/lib/logger.h b/source/lib/logger.h new file mode 100644 index 0000000..3a10f0e --- /dev/null +++ b/source/lib/logger.h @@ -0,0 +1,34 @@ +#ifndef LOGGER_H_ +#define LOGGER_H_ + +#include +#include +#include + +class Logger +{ +private: + std::vector 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 + void log(const std::string &, const std::string &, Args &&...) const; + +private: + void print(const std::string &) const; +}; + +template +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)...)); +} + +#endif diff --git a/source/lib/objcsourcefile.cpp b/source/lib/objcsourcefile.cpp new file mode 100644 index 0000000..953819f --- /dev/null +++ b/source/lib/objcsourcefile.cpp @@ -0,0 +1,15 @@ +#include +#include "objcsourcefile.h" + +using namespace std; +using namespace Msp; + +void ObjCSourceFile::parse_includes(IO::Base &in) +{ + static Regex r_include("^[ \t]*#(include|import)[ \t]+([\"<].*)[\">]"); + + string line; + while(in.getline(line)) + if(RegMatch match = r_include.match(line)) + includes.push_back(match[2].str); +} diff --git a/source/lib/objcsourcefile.h b/source/lib/objcsourcefile.h new file mode 100644 index 0000000..37f79a3 --- /dev/null +++ b/source/lib/objcsourcefile.h @@ -0,0 +1,20 @@ +#ifndef OBJCSOURCEFILE_H_ +#define OBJCSOURCEFILE_H_ + +#include "csourcefile.h" + +/** +Represents an Objective-C source file. +*/ +class ObjCSourceFile: public CSourceFile +{ +public: + using CSourceFile::CSourceFile; + + const char *get_type() const override { return "ObjCSourceFile"; } + +protected: + void parse_includes(Msp::IO::Base &) override; +}; + +#endif diff --git a/source/lib/objectfile.cpp b/source/lib/objectfile.cpp new file mode 100644 index 0000000..00fa679 --- /dev/null +++ b/source/lib/objectfile.cpp @@ -0,0 +1,110 @@ +#include +#include +#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(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 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(&ObjectFile::find_dependencies))); + } +} + +void ObjectFile::find_dependencies(FileTarget &tgt, vector &headers) +{ + tgt.prepare(); + + FileTarget *rtgt = dynamic_cast(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(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(d)) + { + auto i = lower_bound(headers, file); + if(i==headers.end() || *i!=file) + { + headers.insert(i, file); + find_dependencies(*file, headers); + } + } +} diff --git a/source/lib/objectfile.h b/source/lib/objectfile.h new file mode 100644 index 0000000..9e3ff90 --- /dev/null +++ b/source/lib/objectfile.h @@ -0,0 +1,37 @@ +#ifndef OBJECTFILE_H_ +#define OBJECTFILE_H_ + +#include "filetarget.h" + +class SourceFile; + +/** +Object files are compiled from source files. +*/ +class ObjectFile: public FileTarget +{ +private: + SourceFile &source; + bool used_in_shlib = false; + +public: + ObjectFile(Builder &, const Component &, SourceFile &); +private: + static Msp::FS::Path generate_target_path(const Component &, const Msp::FS::Path &); + +public: + const char *get_type() const override { return "ObjectFile"; } + SourceFile &get_source() const { return source; } + + void set_used_in_shared_library(bool); + bool is_used_in_shared_library() const { return used_in_shlib; } + + void collect_build_info(BuildInfo &) const override; + +private: + void find_dependencies() override; + + void find_dependencies(FileTarget &, std::vector &); +}; + +#endif diff --git a/source/lib/package.cpp b/source/lib/package.cpp new file mode 100644 index 0000000..10c2db0 --- /dev/null +++ b/source/lib/package.cpp @@ -0,0 +1,44 @@ +#include +#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(p), + ArchitectureConditional(p.builder, p.name) +{ + add("label", &Package::label); + add("require", &Loader::require); +} + +void Package::Loader::require(const string &n) +{ + Package *req = obj.builder.get_package_manager().find_package(n); + if(req) + obj.requires.push_back(req); + else + obj.problems.push_back(format("Required package %s not found", n)); +} diff --git a/source/lib/package.h b/source/lib/package.h new file mode 100644 index 0000000..d13aa73 --- /dev/null +++ b/source/lib/package.h @@ -0,0 +1,74 @@ +#ifndef PACKAGE_H_ +#define PACKAGE_H_ + +#include +#include +#include +#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, public ArchitectureConditional + { + public: + Loader(Package &); + private: + void require(const std::string &); + }; + + using Requirements = std::vector; + +protected: + Builder &builder; + + std::string name; + std::string label; + + Requirements requires; + BuildInfo export_binfo; + bool prepared = false; + std::vector 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 &get_problems() const { return problems; } + + virtual void save_caches() { } +}; + +#endif diff --git a/source/lib/packagemanager.cpp b/source/lib/packagemanager.cpp new file mode 100644 index 0000000..37374b6 --- /dev/null +++ b/source/lib/packagemanager.cpp @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 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 files = list_filtered(p, "\\.bpk$"); + for(const string &f: files) + binpkg_files.push_back(p/f); + builder.get_logger().log("packagemgr", "%d binary packages found in %s", files.size(), p); + } + + builder.get_logger().log("packagemgr", "%d binary packages found", binpkg_files.size()); + } + + auto i = find_if(binpkg_files, [&name](const FS::Path &p){ return FS::basepart(FS::basename(p))==name; }); + if(i!=binpkg_files.end()) + return *i; + + return FS::Path(); +} + +void PackageManager::save_all_caches() const +{ + for(const auto &kvp: packages) + kvp.second->save_caches(); +} diff --git a/source/lib/packagemanager.h b/source/lib/packagemanager.h new file mode 100644 index 0000000..fc0aa98 --- /dev/null +++ b/source/lib/packagemanager.h @@ -0,0 +1,75 @@ +#ifndef PACKAGEMANAGER_H_ +#define PACKAGEMANAGER_H_ + +#include +#include +#include +#include +#include + +class Builder; +class Package; + +/** +Keeps track of packages. Also responsible for locating previously unknown +packages by name. +*/ +class PackageManager +{ +private: + Builder &builder; + std::vector pkg_path; + std::vector pkg_dirs; + std::vector binpkg_path; + std::vector binpkg_files; + bool no_externals = false; + std::map packages; + Package *main_pkg = 0; + std::set 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 &get_packages() const { return packages; } + + /** Locates a package and loads it if necessary. */ + Package *find_package(const std::string &); + +private: + std::string run_pkgconfig(const std::string &, const std::string &); + + /** Determines the source directory of a package. Pkg-config is consulted + first, and if it fails, the package path is searched for matches. The + package is expected to be located in a directory named after itself. */ + Msp::FS::Path get_package_location(const std::string &); + + /** Determines the file containing a binary package. The file is expected + to be named after the package. */ + Msp::FS::Path get_binary_package_file(const std::string &); + +public: + void save_all_caches() const; +}; + +#endif diff --git a/source/lib/pattern.cpp b/source/lib/pattern.cpp new file mode 100644 index 0000000..fec83da --- /dev/null +++ b/source/lib/pattern.cpp @@ -0,0 +1,31 @@ +#include +#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 Pattern::apply_list(const vector &patterns, const string &body) +{ + vector result; + for(const Pattern &p: patterns) + result.push_back(p.apply(body)); + return result; +} diff --git a/source/lib/pattern.h b/source/lib/pattern.h new file mode 100644 index 0000000..8c623bd --- /dev/null +++ b/source/lib/pattern.h @@ -0,0 +1,30 @@ +#ifndef PATTERN_H_ +#define PATTERN_H_ + +#include +#include + +/** +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 apply_list(const std::vector &, const std::string &); +}; + +#endif diff --git a/source/lib/sharedlibrary.cpp b/source/lib/sharedlibrary.cpp new file mode 100644 index 0000000..0673a05 --- /dev/null +++ b/source/lib/sharedlibrary.cpp @@ -0,0 +1,69 @@ +#include +#include +#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 &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(*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(format("%s-%s", libname, version)); + else if(arch.get_system()=="darwin") + soname = arch.create_filename(format("%s.%s", libname, version)); + else + soname = format("%s.%s", arch.create_filename(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(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(comp.get_name()); + } +} + +void SharedLibrary::set_import_library(ImportLibrary *imp) +{ + import_lib = imp; +} diff --git a/source/lib/sharedlibrary.h b/source/lib/sharedlibrary.h new file mode 100644 index 0000000..564b210 --- /dev/null +++ b/source/lib/sharedlibrary.h @@ -0,0 +1,39 @@ +#ifndef SHAREDLIB_H_ +#define SHAREDLIB_H_ + +#include "binary.h" + +class ImportLibrary; + +/** +Represents a shared library. It has two special properties: libname and +soname. Libname is the name used by the linker. Soname is the canonical +filename of the library, including version number. If the owning package has +no version, soname will be empty. + +A SharedLibrary can also store a pointer to the associated ImportLibrary, for +platforms that need one. +*/ +class SharedLibrary: public Binary +{ +private: + std::string libname; + std::string soname; + ImportLibrary *import_lib = 0; + +public: + SharedLibrary(Builder &, const Msp::FS::Path &); + SharedLibrary(Builder &, const Component &, const std::vector &); +private: + static std::string generate_filename(const Component &); + +public: + const char *get_type() const override { return "SharedLibrary"; } + const std::string &get_libname() const { return libname; } + const std::string &get_soname() const { return soname; } + + void set_import_library(ImportLibrary *); + ImportLibrary *get_import_library() const { return import_lib; } +}; + +#endif diff --git a/source/lib/sourcearchivecomponent.cpp b/source/lib/sourcearchivecomponent.cpp new file mode 100644 index 0000000..b1e4279 --- /dev/null +++ b/source/lib/sourcearchivecomponent.cpp @@ -0,0 +1,43 @@ +#include +#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 files; + files.push_back(&package.get_build_file()); + + for(const FS::Path &s: collect_source_files()) + { + FileTarget *file = builder.get_vfs().get_target(s); + if(!file) + file = new File(builder, package, s); + files.push_back(file); + } + + BuildGraph &build_graph = builder.get_build_graph(); + for(const auto &kvp: build_graph.get_targets()) + if(kvp.second->get_package()==&package && !kvp.second->is_buildable()) + if(!any_equals(files, kvp.second)) + files.push_back(kvp.second); + + const Toolchain &toolchain = builder.get_toolchain(); + string archive_name = package.get_name(); + if(!package.get_version().empty()) + archive_name += "-"+package.get_version(); + archive_name += "-source"; + Target *result = toolchain.get_tool("TAR").create_target(files, archive_name); + build_graph.get_target("archives")->add_dependency(*result); +} diff --git a/source/lib/sourcearchivecomponent.h b/source/lib/sourcearchivecomponent.h new file mode 100644 index 0000000..94c1bc5 --- /dev/null +++ b/source/lib/sourcearchivecomponent.h @@ -0,0 +1,14 @@ +#ifndef TARBALLCOMPONENT_H_ +#define TARBALLCOMPONENT_H_ + +#include "component.h" + +class SourceArchiveComponent: public Component +{ +public: + SourceArchiveComponent(SourcePackage &); + + void create_targets() const override; +}; + +#endif diff --git a/source/lib/sourcefile.cpp b/source/lib/sourcefile.cpp new file mode 100644 index 0000000..b5b84c7 --- /dev/null +++ b/source/lib/sourcefile.cpp @@ -0,0 +1,9 @@ +#include "component.h" +#include "sourcefile.h" +#include "sourcepackage.h" + +SourceFile::SourceFile(Builder &b, const Component &c, const Msp::FS::Path &p): + FileTarget(b, c.get_package(), p) +{ + component = &c; +} diff --git a/source/lib/sourcefile.h b/source/lib/sourcefile.h new file mode 100644 index 0000000..16521a5 --- /dev/null +++ b/source/lib/sourcefile.h @@ -0,0 +1,13 @@ +#ifndef SOURCEFILE_H_ +#define SOURCEFILE_H_ + +#include "filetarget.h" + +class SourceFile: public FileTarget +{ +protected: + SourceFile(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { } + SourceFile(Builder &, const Component &, const Msp::FS::Path &); +}; + +#endif diff --git a/source/lib/sourcegenerator.cpp b/source/lib/sourcegenerator.cpp new file mode 100644 index 0000000..d30a2bc --- /dev/null +++ b/source/lib/sourcegenerator.cpp @@ -0,0 +1,132 @@ +#include +#include +#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 &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(*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(*out_src.get_tool()); + + vector 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(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(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(&SourceGenerator::processing_unit)); +} + +void SourceGenerator::Loader::argument(const string &a) +{ + obj.arguments.push_back(a); +} + +void SourceGenerator::Loader::arguments(const vector &a) +{ + obj.arguments.insert(obj.arguments.end(), a.begin(), a.end()); +} + +void SourceGenerator::Loader::command(const string &c) +{ + if(c.find('/')!=string::npos) + obj.set_command((obj.package.get_source_directory()/c).str()); + else + obj.set_command(c); +} + +void SourceGenerator::Loader::in_suffix(const string &s) +{ + obj.input_suffixes.push_back(s); +} + +void SourceGenerator::Loader::out_suffix(const string &s) +{ + obj.out_suffixes.push_back(s); +} diff --git a/source/lib/sourcegenerator.h b/source/lib/sourcegenerator.h new file mode 100644 index 0000000..b8dee24 --- /dev/null +++ b/source/lib/sourcegenerator.h @@ -0,0 +1,44 @@ +#ifndef SOURCEGENERATOR_H_ +#define SOURCEGENERATOR_H_ + +#include +#include "conditionalloader.h" +#include "sourcepackage.h" +#include "tool.h" + +class SourceFile; + +class SourceGenerator: public Tool +{ +public: + class Loader: public Msp::DataFile::ObjectLoader, public ConditionalLoader + { + public: + Loader(SourceGenerator &); + + private: + void argument(const std::string &); + void arguments(const std::vector &); + 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 out_suffixes; + std::vector 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 &, const std::string &) override; + +private: + static ExternalTask::Arguments _run(const SourceFile &, Msp::FS::Path &); +}; + +#endif diff --git a/source/lib/sourcepackage.cpp b/source/lib/sourcepackage.cpp new file mode 100644 index 0000000..3648df7 --- /dev/null +++ b/source/lib/sourcepackage.cpp @@ -0,0 +1,273 @@ +#include +#include +#include +#include +#include +#include +#include +#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(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(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(p), + FeatureConditional(p, p.name), + options(o) +{ + add("android_application", &Loader::component); + add("build_info", &Loader::build_info); + add("datapack", &Loader::component); + add("description", &SourcePackage::description); + add("feature", &Loader::feature); + add("generate", &Loader::generate); + add("install", &Loader::component); + add("interface_version", &Loader::interface_version); + add("library", &Loader::component_arg, BinaryComponent::LIBRARY); + add("module", &Loader::component_arg, BinaryComponent::MODULE); + add("program", &Loader::component_arg, 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 +void SourcePackage::Loader::component(const string &n) +{ + C *comp = new C(obj, n); + load_sub(*comp); + obj.components.push_back(comp); +} + +template +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=2) + break; + obj.interface_version = obj.version.substr(0, i); +} diff --git a/source/lib/sourcepackage.h b/source/lib/sourcepackage.h new file mode 100644 index 0000000..8be8ada --- /dev/null +++ b/source/lib/sourcepackage.h @@ -0,0 +1,93 @@ +#ifndef SOURCEPACKAGE_H_ +#define SOURCEPACKAGE_H_ + +#include +#include +#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, 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 + void component(const std::string &); + template + 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 features; + BuildInfo build_info; + std::vector components; + SourceArchiveComponent *source_archive; + Config config; + mutable Cache cache; + +public: + SourcePackage(Builder &, const std::string &, const Msp::FS::Path &); + ~SourcePackage(); + + const std::string &get_version() const { return version; } + const std::string &get_interface_version() const { return interface_version; } + const std::string &get_description() const { return description; } + + FileTarget &get_build_file() const { return *build_file; } + const Msp::FS::Path &get_source_directory() const { return source_dir; } + Msp::FS::Path get_temp_directory() const; + Msp::FS::Path get_output_directory() const; + + const Toolchain &get_toolchain() const { return local_tools; } + const Component &get_component(const std::string &) const; + const Config &get_config() const { return config; } + bool match_feature(const std::string &, const std::string *) const; + void set_build_type(const BuildType &); + const BuildInfo &get_build_info() const { return build_info; } +private: + void do_prepare() override; + +public: + Cache &get_cache() const { return cache; } +private: + void save_caches() override; +}; + +#endif diff --git a/source/lib/staticlibrary.cpp b/source/lib/staticlibrary.cpp new file mode 100644 index 0000000..039eb8e --- /dev/null +++ b/source/lib/staticlibrary.cpp @@ -0,0 +1,42 @@ +#include "builder.h" +#include "component.h" +#include "objectfile.h" +#include "sourcepackage.h" +#include "staticlibrary.h" + +using namespace std; +using namespace Msp; + +StaticLibrary::StaticLibrary(Builder &b, const Component &c, const vector &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(comp.get_name()); +} + +void StaticLibrary::add_required_library(const string &lib) +{ + build_info.libs.push_back(lib); +} + +void StaticLibrary::add_library_path(const FS::Path &pth) +{ + build_info.libpath.push_back(pth); +} + +void StaticLibrary::collect_build_info(BuildInfo &binfo) const +{ + Target::collect_build_info(binfo); + binfo.update_from(build_info); +} diff --git a/source/lib/staticlibrary.h b/source/lib/staticlibrary.h new file mode 100644 index 0000000..98d2990 --- /dev/null +++ b/source/lib/staticlibrary.h @@ -0,0 +1,34 @@ +#ifndef STATICLIBRARY_H_ +#define STATICLIBRARY_H_ + +#include "filetarget.h" + +class Component; +class ObjectFile; + +/** +A static library target. +*/ +class StaticLibrary: public FileTarget +{ +private: + /* TODO this really belongs in a Component, but some refactoring is required + to allow non-builder packages to have components. Rename BinaryPackage to + ExternalPackage, add BuildableComponent and ExternalComponent classes. */ + BuildInfo build_info; + +public: + StaticLibrary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { } + StaticLibrary(Builder &, const Component &, const std::vector &); +private: + static std::string generate_filename(const Component &); + +public: + const char *get_type() const override { return "StaticLibrary"; } + + void add_required_library(const std::string &); + void add_library_path(const Msp::FS::Path &); + void collect_build_info(BuildInfo &) const override; +}; + +#endif diff --git a/source/lib/sysutils.cpp b/source/lib/sysutils.cpp new file mode 100644 index 0000000..95867b7 --- /dev/null +++ b/source/lib/sysutils.cpp @@ -0,0 +1,113 @@ +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#define WIN32_LEAN_AND_MEAN +#define INITGUID +#ifdef _WIN32 +#include +#include +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#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( + string(reinterpret_cast(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(const string &path) +{ +#ifdef _WIN32 + string::size_type first_sep = path.find('\\'); + string::size_type last_sep = path.rfind('\\'); + string root = path.substr(0, first_sep); + string key_path = path.substr(first_sep+1, last_sep-first_sep-1); + string value_name = path.substr(last_sep+1); + + HKEY root_handle; + if(root=="HKCR") + root_handle = HKEY_CLASSES_ROOT; + else if(root=="HKCC") + root_handle = HKEY_CURRENT_CONFIG; + else if(root=="HKCU") + root_handle = HKEY_CURRENT_USER; + else if(root=="HKLM") + root_handle = HKEY_LOCAL_MACHINE; + else if(root=="HKU") + root_handle = HKEY_USERS; + else + throw invalid_argument("get_registry_value"); + + HKEY key; + LSTATUS err = RegOpenKeyEx(root_handle, key_path.c_str(), 0, KEY_READ, &key); + if(err!=ERROR_SUCCESS) + throw Msp::system_error("RegOpenKey", err); + + DWORD value_len; + err = RegGetValue(key, 0, value_name.c_str(), RRF_RT_REG_SZ, 0, 0, &value_len); + if(err!=ERROR_SUCCESS) + throw Msp::system_error("RegGetValue", err); + + char *buffer = new char[value_len]; + err = RegGetValue(key, 0, value_name.c_str(), RRF_RT_REG_SZ, 0, buffer, &value_len); + if(err!=ERROR_SUCCESS) + { + delete[] buffer; + throw Msp::system_error("RegGetValue", err); + } + + string result(buffer); + delete[] buffer; + return result; +#else + (void)path; + return string(); +#endif +} diff --git a/source/lib/sysutils.h b/source/lib/sysutils.h new file mode 100644 index 0000000..7300869 --- /dev/null +++ b/source/lib/sysutils.h @@ -0,0 +1,13 @@ +#ifndef SYSUTILS_H_ +#define SYSUTILS_H_ + +#include +#include + +std::string get_system_type(); +Msp::FS::Path get_program_files_x86_dir(); + +template +T get_registry_value(const std::string &); + +#endif diff --git a/source/lib/target.cpp b/source/lib/target.cpp new file mode 100644 index 0000000..dbdd950 --- /dev/null +++ b/source/lib/target.cpp @@ -0,0 +1,193 @@ +#include +#include +#include +#include "builder.h" +#include "filetarget.h" +#include "sourcepackage.h" +#include "target.h" +#include "task.h" +#include "tool.h" + +using namespace std; +using namespace Msp; + +Target::Target(Builder &b, const string &n): + builder(b), + name(n) +{ + builder.get_build_graph().add_target(this); +} + +void Target::add_dependency(Target &dep) +{ + if(&dep==this) + throw invalid_argument("Target::add_depend"); + depends.push_back(&dep); + if(state>PREPARING) + dep.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild)); +} + +void Target::add_transitive_dependency(Target &dep) +{ + if(&dep==this) + throw invalid_argument("Target::add_transitive_dependency"); + trans_depends.push_back(&dep); +} + +void Target::add_side_effect(Target &se) +{ + side_effects.push_back(&se); + if(tool) + se.set_tool(*tool); + se.primary_target = this; + /* Side effects are checked for rebuild after the primary target. Recheck + the primary if a side effect is marked for rebuild. */ + se.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild)); +} + +Target *Target::get_buildable_target() +{ + if(primary_target) + return primary_target->get_buildable_target(); + if(!needs_rebuild()) + return 0; + + bool self_ok = state!=BUILDING; + for(Target *d: depends) + { + // Avoid infinite recursion if a target depends on its own side effect + if(any_equals(side_effects, d)) + continue; + + Target *tgt = d->get_buildable_target(); + if(tgt) + return tgt; + else if(d->needs_rebuild()) + self_ok = false; + } + + if(self_ok) + return this; + + return 0; +} + +void Target::set_tool(Tool &t) +{ + tool = &t; + for(Target *s: side_effects) + s->set_tool(t); +} + +void Target::collect_build_info(BuildInfo &binfo) const +{ + if(tool) + binfo.update_from(tool->get_build_info()); + if(component) + binfo.update_from(component->get_build_info()); + else if(package) + binfo.update_from(package->get_build_info()); +} + +void Target::force_rebuild() +{ + if(!is_buildable()) + throw logic_error("Target::force_rebuild"); + mark_rebuild("Forced rebuild"); +} + +void Target::mark_rebuild(const string &reason) +{ + if(reason.empty()) + throw invalid_argument("No reason given for rebuilding "+name); + + state = REBUILD; + rebuild_reason = reason; + + builder.get_logger().log("rebuild", "Rebuilding %s: %s", name, reason); + + signal_bubble_rebuild.emit(); +} + +void Target::prepare() +{ + if(state>PREPARING) + return; + if(state==PREPARING) + { + builder.get_logger().log("problems", "Dependency cycle detected at %s", name); + problems.push_back("Dependency cycle detected"); + state = BROKEN; + return; + } + + state = PREPARING; + /* Prepare existing dependencies early, because their information may be + needed to find other dependencies. */ + for(Target *d: depends) + d->prepare(); + if(tool) + tool->prepare(); + + find_dependencies(); + bool broken = !problems.empty(); + + if(tool) + { + if(FileTarget *tool_exe = tool->get_executable()) + add_dependency(*tool_exe); + broken |= !tool->get_problems().empty(); + + // Only check package and component problems for buildable targets + // XXX How to propagate nested package problems? + broken |= (package && !package->get_problems().empty()); + broken |= (component && !component->get_problems().empty()); + } + + /* Now that all dependencies are known, prepare them again. This will do + nothing to already prepared targets. */ + for(Target *d: depends) + { + d->prepare(); + broken |= d->is_broken(); + } + for(Target *d: trans_depends) + d->prepare(); + + check_rebuild(); + if(broken) + state = BROKEN; + else if(state==PREPARING) + state = UPTODATE; + + for(Target *d: depends) + d->signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild)); +} + +Task *Target::build() +{ + if(primary_target) + return primary_target->build(); + + Task *task = tool->run(*this); + task->signal_finished.connect(sigc::mem_fun(this, &Target::build_finished)); + state = BUILDING; + + build(*task); + for(Target *s: side_effects) + s->build(*task); + + return task; +} + +void Target::build_finished(bool success) +{ + state = UPTODATE; + if(success) + { + modified(); + for(Target *s: side_effects) + s->build_finished(success); + signal_modified.emit(); + } +} diff --git a/source/lib/target.h b/source/lib/target.h new file mode 100644 index 0000000..6ef6457 --- /dev/null +++ b/source/lib/target.h @@ -0,0 +1,169 @@ +#ifndef TARGET_H_ +#define TARGET_H_ + +#include +#include +#include +#include +#include +#include + +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; + +protected: + enum State + { + INIT, + PREPARING, + REBUILD, + BUILDING, + UPTODATE, + BROKEN + }; + +public: + sigc::signal signal_bubble_rebuild; + sigc::signal 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 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 &get_problems() const { return problems; } + + /** Prepares the target by finding dependencies, recursively preparing them + and then checking whether rebuilding is needed. */ + void prepare(); + + /** Invokes the associated Tool to build the target and returns the + resulting Task. The task must be started by the caller. */ + virtual Task *build(); + +protected: + /** Targets can override this to do additional setup on the Task. This is + also called on side effects, which normally do not get built by themselves. */ + virtual void build(Task &) { } + + /** Handler for Task::signal_finished. */ + virtual void build_finished(bool); + + virtual void modified() { } + +public: + /** Removes any results of building the target. */ + virtual void clean() { } +}; + +#endif diff --git a/source/lib/task.cpp b/source/lib/task.cpp new file mode 100644 index 0000000..e72a741 --- /dev/null +++ b/source/lib/task.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include "task.h" + +using namespace std; +using namespace Msp; + +void Task::add_file(const FS::Path &f) +{ + files.push_back(f); +} + +void Task::set_unlink(bool u) +{ + unlink = u; +} + +void Task::prepare() +{ + for(const FS::Path &f: files) + { + if(FS::exists(f)) + { + // If the file exists, the directory it's in must exist too + FS::unlink(f); + } + else + { + FS::Path dir = FS::dirname(f); + if(!FS::exists(dir)) + FS::mkpath(dir, 0755); + } + } +} diff --git a/source/lib/task.h b/source/lib/task.h new file mode 100644 index 0000000..915ffe1 --- /dev/null +++ b/source/lib/task.h @@ -0,0 +1,59 @@ +#ifndef TASK_H_ +#define TASK_H_ + +#include +#include +#include + +/** +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 signal_finished; + +protected: + std::vector files; + bool unlink = false; + + Task() = default; +public: + virtual ~Task() { } + + /** Associate the task with a file. */ + void add_file(const Msp::FS::Path &); + + /** If set to true, the associated files are removed before the task is + started. */ + void set_unlink(bool = true); + + /** Returns the command being executed for this task. Only makes sense if + an external command is involved. */ + virtual std::string get_command() const = 0; + + /// Starts the task. + virtual void start() = 0; + +protected: + /// Ensures that the output directory exists and removes files if necessary. + void prepare(); + +public: + /// Checks the status of the task and immediately returns. + virtual Status check() = 0; + + /// Waits for the task to finish and returns its final status. + virtual Status wait() = 0; +}; + +#endif diff --git a/source/lib/templatefile.h b/source/lib/templatefile.h new file mode 100644 index 0000000..0912025 --- /dev/null +++ b/source/lib/templatefile.h @@ -0,0 +1,17 @@ +#ifndef TEMPLATEFILE_H_ +#define TEMPLATEFILE_H_ + +#include "sourcefile.h" + +/** +Input file for SourceGenerator. +*/ +class TemplateFile: public SourceFile +{ +public: + TemplateFile(Builder &b, const Component &c, const Msp::FS::Path &p): SourceFile(b, c, p) { } + + const char *get_type() const override { return "TemplateFile"; } +}; + +#endif diff --git a/source/lib/tool.cpp b/source/lib/tool.cpp new file mode 100644 index 0000000..eaf4977 --- /dev/null +++ b/source/lib/tool.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#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 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 sources; + sources.push_back(&source); + return create_target(sources, arg); +} + +void Tool::prepare() +{ + if(prepared) + return; + + prepared = true; + + if(!command.empty()) + executable = builder.get_vfs().find_binary(command); + prepare(*this); + if(!command.empty() && !executable) + { + builder.get_logger().log("problems", "Can't find executable %s for %s", command, tag); + problems.push_back(format("Can't find executable %s", command)); + } +} + +void Tool::prepare(Tool &tool) const +{ + if(&tool!=this && tool.get_base_tool()!=this) + throw invalid_argument("Tool::prepare"); + + if(&tool!=this && !command.empty() && tool.command.empty()) + throw logic_error("Derived tool has no command"); + + do_prepare(tool); +} + +string Tool::create_build_signature(const BuildInfo &) const +{ + if(executable) + return format("%s=%s", tag, FS::basename(executable->get_path())); + else + return string(); +} + + +void operator>>(const LexicalConverter &conv, Tool::ProcessingUnit &unit) +{ + const string &str = conv.get(); + if(str=="FILE") + unit = Tool::ONE_FILE; + else if(str=="DIRECTORY") + unit = Tool::DIRECTORY; + else if(str=="COMPONENT") + unit = Tool::COMPONENT; + else + throw lexical_error(format("conversion of '%s' to ProcessingUnit", str)); +} diff --git a/source/lib/tool.h b/source/lib/tool.h new file mode 100644 index 0000000..b756563 --- /dev/null +++ b/source/lib/tool.h @@ -0,0 +1,175 @@ +#ifndef TOOL_H_ +#define TOOL_H_ + +#include +#include +#include +#include +#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 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 input_suffixes; + std::vector aux_suffixes; + ProcessingUnit processing_unit = ONE_FILE; + std::function 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); + + template + void set_run_external(ExternalTask::Arguments (*)(const T &, Msp::FS::Path &)); + + template + 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 &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 &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 &, 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 &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 +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(t), work_dir); + return new ExternalTask(args, work_dir); + }); +} + +template +void Tool::set_run_internal(bool (*f)(const T &)) +{ + set_run([f](const Target &t){ + const T &ct = dynamic_cast(t); + return new InternalTask([f, &ct]{ return f(ct); }); + }); +} + + +void operator>>(const Msp::LexicalConverter &, Tool::ProcessingUnit &); + +#endif diff --git a/source/lib/toolchain.cpp b/source/lib/toolchain.cpp new file mode 100644 index 0000000..c5b8caa --- /dev/null +++ b/source/lib/toolchain.cpp @@ -0,0 +1,58 @@ +#include +#include +#include "tool.h" +#include "toolchain.h" + +using namespace std; +using namespace Msp; + +Toolchain::~Toolchain() +{ + for(const auto &kvp: tools) + delete kvp.second; + for(Toolchain *c: chains) + delete c; +} + +void Toolchain::add_tool(Tool *tool) +{ + insert_unique(tools, tool->get_tag(), tool); +} + +void Toolchain::add_toolchain(Toolchain *chain) +{ + auto i = upper_bound(chains, chain->get_priority(), [](int p, Toolchain *tc){ return p>tc->get_priority(); }); + chains.insert(i, chain); +} + +bool Toolchain::has_tool(const string &tag) const +{ + if(tools.count(tag)) + return true; + return any_of(chains.begin(), chains.end(), [&tag](Toolchain *tc){ return tc->has_tool(tag); }); +} + +Tool &Toolchain::get_tool(const string &tag) const +{ + if(!tools.count(tag)) + { + for(const Toolchain *c: chains) + if(c->has_tool(tag)) + return c->get_tool(tag); + } + + return *get_item(tools, tag); +} + +Tool *Toolchain::get_tool_for_suffix(const string &suffix, bool aux) const +{ + for(const auto &kvp: tools) + if(kvp.second->accepts_suffix(suffix, aux)) + return kvp.second; + + for(const Toolchain *c: chains) + if(Tool *tool = c->get_tool_for_suffix(suffix, aux)) + return tool; + + return 0; +} diff --git a/source/lib/toolchain.h b/source/lib/toolchain.h new file mode 100644 index 0000000..34fc0a2 --- /dev/null +++ b/source/lib/toolchain.h @@ -0,0 +1,37 @@ +#ifndef TOOLCHAIN_H_ +#define TOOLCHAIN_H_ + +#include +#include +#include + +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 tools; + std::vector 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 &get_toolchains() { return chains; } +}; + +#endif diff --git a/source/lib/virtualfilesystem.cpp b/source/lib/virtualfilesystem.cpp new file mode 100644 index 0000000..16d4de8 --- /dev/null +++ b/source/lib/virtualfilesystem.cpp @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include +#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(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(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 shared_names; + bool use_import_lib = false; + if(mode!=BuildInfo::FORCE_STATIC) + { + shared_names = Pattern::apply_list(arch.get_patterns(), lib); + if(!(use_import_lib = !shared_names.empty())) + shared_names = Pattern::apply_list(arch.get_patterns(), lib); + } + + vector static_names; + if(mode!=BuildInfo::FORCE_DYNAMIC) + static_names = Pattern::apply_list(arch.get_patterns(), lib); + + for(const FS::Path &p: combined_path) + { + const vector *cur_names = (mode>=BuildInfo::DYNAMIC ? &shared_names : &static_names); + for(auto j=cur_names->begin(); j!=cur_names->end(); ) + { + FS::Path filename = p / *j; + if(FileTarget *tgt = get_target(filename)) + { + builder.get_logger().log("vfs", "Library %s (%s) found in %s as existing %s", lib, *j, p.str(), tgt->get_type()); + return tgt; + } + else if(file_exists(filename)) + { + builder.get_logger().log("vfs", "Library %s (%s) found in %s", lib, *j, p.str()); + if(cur_names==&shared_names) + { + if(use_import_lib) + return new ImportLibrary(builder, filename); + return new SharedLibrary(builder, filename); + } + else + return new StaticLibrary(builder, filename); + } + + if(++j==cur_names->end()) + { + if(mode==BuildInfo::DYNAMIC && cur_names==&shared_names) + cur_names = &static_names; + else if(mode==BuildInfo::STATIC && cur_names==&static_names) + cur_names = &shared_names; + else + break; + j = cur_names->begin(); + } + } + + builder.get_logger().log("vfs", "Library %s not found in %s", lib, p.str()); + } + + return 0; +} + +FileTarget *VirtualFileSystem::find_binary(const string &name) +{ + SearchPath path; + if(FS::Path(name).is_absolute()) + path.push_back("/"); + else + { + if(sys_bin_path.empty()) + { + string env_path = Msp::getenv("PATH"); + if(!env_path.empty()) + { + for(const string &p: split(env_path, ':')) + sys_bin_path.push_back(p); + } + else + { + sys_bin_path.push_back("/bin"); + sys_bin_path.push_back("/usr/bin"); + } + } + path = sys_bin_path; + } + + for(const FS::Path &p: path) + { + FS::Path filename = p/name; + if(FileTarget *tgt = get_target(filename)) + { + builder.get_logger().log("vfs", "Binary %s found in %s as existing %s", name, p, tgt->get_type()); + return tgt; + } + else if(file_exists(filename)) + { + builder.get_logger().log("vfs", "Binary %s found in %s", name, p); + return new Executable(builder, filename); + } + + builder.get_logger().log("vfs", "Binary %s not found in %s", name, p); + } + + return 0; +} + +bool VirtualFileSystem::file_exists(const FS::Path &filename) +{ + if(nonexistent.count(filename)) + return false; + if(FS::is_reg(filename)) + return true; + nonexistent.insert(filename); + return false; +} diff --git a/source/lib/virtualfilesystem.h b/source/lib/virtualfilesystem.h new file mode 100644 index 0000000..60e198a --- /dev/null +++ b/source/lib/virtualfilesystem.h @@ -0,0 +1,63 @@ +#ifndef VIRTUALFILESYSTEM_H_ +#define VIRTUALFILESYSTEM_H_ + +#include +#include +#include +#include +#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; + +private: + Builder &builder; + std::map targets; + std::set nonexistent; + SearchPath sys_bin_path; + +public: + VirtualFileSystem(Builder &b): builder(b) { } + + /** Gets an existing target associated with a path. If no target has claimed + that path, 0 is returned. */ + FileTarget *get_target(const Msp::FS::Path &) const; + + /** Registers a target with the VFS. A target may be registered at multiple + paths if building it results in multiple files. */ + void register_path(const Msp::FS::Path &, FileTarget *); + + /** Locates a source file. If a file is found but no target is associated + with it, a new package-less target is created with the appropriate tool. If + use_syspath is true, the system path reported by the tool is also searched. */ + FileTarget *find_header(const std::string &, Tool *, const SearchPath &, bool use_syspath = true); + + /** Locates a library. The library name should be the same as what would be + used in linking with the library. If a file is found but no target is + associated with it, a new package-less target of appropriate type is + created. If use_syspath is true, the system path reported by the LINK tool + is also searched. */ + FileTarget *find_library(const std::string &, const SearchPath &, BuildInfo::LibraryMode, bool use_syspath = true); + + /** Locates a binary. The normal search path for binaries is used (usually + this means the PATH environment variable). If a file is found but no target + is associated with it, a new package-less Executable target is created. */ + FileTarget *find_binary(const std::string &); + +private: + bool file_exists(const Msp::FS::Path &); +}; + +#endif diff --git a/source/lib/virtualtarget.cpp b/source/lib/virtualtarget.cpp new file mode 100644 index 0000000..07177d8 --- /dev/null +++ b/source/lib/virtualtarget.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +#include "builder.h" +#include "virtualtarget.h" + +using namespace std; +using namespace Msp; + +void VirtualTarget::check_rebuild() +{ + // Virtual targets are only rebuilt if their dependencies need rebuilding. + auto i = find_if(depends, [](Target *d){ return d->needs_rebuild(); }); + if(i!=depends.end()) + mark_rebuild((*i)->get_name()+" needs rebuilding"); +} + +Task *VirtualTarget::build() +{ + state = UPTODATE; + return 0; +} diff --git a/source/lib/virtualtarget.h b/source/lib/virtualtarget.h new file mode 100644 index 0000000..5151826 --- /dev/null +++ b/source/lib/virtualtarget.h @@ -0,0 +1,22 @@ +#ifndef VIRTUALTARGET_H_ +#define VIRTUALTARGET_H_ + +#include "target.h" + +/** +A target that is not associated with any file. +*/ +class VirtualTarget: public Target +{ +public: + VirtualTarget(Builder &b, const std::string &n): Target(b, n) { } + + const char *get_type() const override { return "VirtualTarget"; } +private: + void check_rebuild() override; + +public: + Task *build() override; +}; + +#endif diff --git a/source/logger.cpp b/source/logger.cpp deleted file mode 100644 index 35d4507..0000000 --- a/source/logger.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include -#include "logger.h" - -using namespace std; -using namespace Msp; - -void Logger::enable_channel(const string &chan) -{ - auto i = lower_bound(enabled_channels, chan); - if(i==enabled_channels.end() || *i!=chan) - enabled_channels.insert(i, chan); -} - -void Logger::disable_channel(const string &chan) -{ - auto i = lower_bound(enabled_channels, chan); - if(i!=enabled_channels.end() && *i==chan) - enabled_channels.erase(i); -} - -bool Logger::is_channel_enabled(const string &chan) const -{ - auto i = lower_bound(enabled_channels, chan); - return (i!=enabled_channels.end() && *i==chan); -} - -void Logger::log(const string &chan, const string &message) const -{ - if(is_channel_enabled(chan)) - print(message); -} - -void Logger::print(const string &message) const -{ - IO::print("%s\n", message); -} diff --git a/source/logger.h b/source/logger.h deleted file mode 100644 index 3a10f0e..0000000 --- a/source/logger.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef LOGGER_H_ -#define LOGGER_H_ - -#include -#include -#include - -class Logger -{ -private: - std::vector 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 - void log(const std::string &, const std::string &, Args &&...) const; - -private: - void print(const std::string &) const; -}; - -template -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)...)); -} - -#endif diff --git a/source/microsofttools.cpp b/source/microsofttools.cpp deleted file mode 100644 index c320ecb..0000000 --- a/source/microsofttools.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include -#include -#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 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("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 sdk_versions = FS::list_filtered(win_sdk_dir/"include", "^10\\."); - if(sdk_versions.empty()) - { - builder.get_logger().log("problems", "No Windows SDK versions found"); - return; - } - - sort(sdk_versions); - win_sdk_version = sdk_versions.back(); - - builder.get_logger().log("tools", "Windows SDK version %s found in %s", win_sdk_version, win_sdk_dir); -} - -int MicrosoftTools::get_priority(const Architecture &arch) -{ - if(arch.get_toolchain()=="msvc") - return 20; - else if(arch.get_system()=="windows") - return 10; - else - return 0; -} diff --git a/source/microsofttools.h b/source/microsofttools.h deleted file mode 100644 index f2e3c37..0000000 --- a/source/microsofttools.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef MICROSOFTTOOLS_H_ -#define MICROSOFTTOOLS_H_ - -#include -#include "toolchain.h" - -class Architecture; -class Builder; - -class MicrosoftTools: public Toolchain -{ -private: - Msp::FS::Path vc_base_dir; - Msp::FS::Path vc_bin_dir; - Msp::FS::Path win_sdk_dir; - std::string win_sdk_version; - -public: - MicrosoftTools(Builder &, const Architecture &); - -private: - void find_vc_bin_dir(Builder &, const Architecture &); - void find_windows_sdk_dir(Builder &); - -public: - const Msp::FS::Path &get_vc_base_dir() const { return vc_base_dir; } - const Msp::FS::Path &get_vc_bin_dir() const { return vc_bin_dir; } - const Msp::FS::Path &get_windows_sdk_dir() const { return win_sdk_dir; } - const std::string &get_windows_sdk_version() const { return win_sdk_version; } - - static int get_priority(const Architecture &); -}; - -#endif diff --git a/source/mingwdlltool.cpp b/source/mingwdlltool.cpp deleted file mode 100644 index c6eddc0..0000000 --- a/source/mingwdlltool.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include -#include -#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 &sources, const string &) -{ - if(sources.size()!=1) - throw invalid_argument("MingwDllTool::create_target"); - SharedLibrary &shlib = dynamic_cast(*sources.front()); - - vector objs; - objs.reserve(shlib.get_dependencies().size()); - for(Target *d: shlib.get_dependencies()) - if(ObjectFile *obj = dynamic_cast(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(&target)) - { - Tool © = builder.get_toolchain().get_tool("CP"); - InstalledFile *inst_tgt = dynamic_cast(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(&target); - const ExportDefinitions *exp = 0; - if(imp) - exp = &dynamic_cast(*imp->get_dependencies().front()); - else - exp = dynamic_cast(&target); - if(!imp && !exp) - throw invalid_argument("MingwDllTool::run"); - - vector 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(d)) - argv.push_back(relative(obj->get_path(), work_dir).str()); - - // XXX Should use dllexport, but that has some other problems to solve - argv.push_back("--export-all-symbols"); - - argv.push_back("-z"); - argv.push_back(relative(exp->get_path(), work_dir).str()); - } - - return new ExternalTask(argv, work_dir); -} diff --git a/source/mingwdlltool.h b/source/mingwdlltool.h deleted file mode 100644 index d9fb1cb..0000000 --- a/source/mingwdlltool.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef DLLTOOL_H_ -#define DLLTOOL_H_ - -#include "tool.h" - -class MingwDllTool: public Tool -{ -public: - MingwDllTool(Builder &, const Architecture &); - - Target *create_target(const std::vector &, const std::string &) override; - Target *create_install(Target &) const override; - -private: - static Task *_run(const Target &); -}; - -#endif diff --git a/source/msvcarchiver.cpp b/source/msvcarchiver.cpp deleted file mode 100644 index d0489cf..0000000 --- a/source/msvcarchiver.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include -#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 &sources, const string &) -{ - if(sources.empty()) - throw invalid_argument("MsvcArchiver::create_target"); - - vector objs; - objs.reserve(sources.size()); - for(Target *s: sources) - objs.push_back(&dynamic_cast(*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 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(d)) - argv.push_back(relative(obj->get_path(), work_dir).str()); - - return argv; -} diff --git a/source/msvcarchiver.h b/source/msvcarchiver.h deleted file mode 100644 index 7af9571..0000000 --- a/source/msvcarchiver.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef MSVCARCHIVER_H_ -#define MSVCARCHIVER_H_ - -#include "tool.h" - -class MicrosoftTools; -class StaticLibrary; - -class MsvcArchiver: public Tool -{ -private: - const MicrosoftTools &ms_tools; - -public: - MsvcArchiver(Builder &, const Architecture &, const MicrosoftTools &); - - Target *create_target(const std::vector &, const std::string &) override; - -private: - static ExternalTask::Arguments _run(const StaticLibrary &, Msp::FS::Path &); -}; - -#endif diff --git a/source/msvccompiler.cpp b/source/msvccompiler.cpp deleted file mode 100644 index 56c99c3..0000000 --- a/source/msvccompiler.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include -#include -#include -#include -#include -#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 &sources, const string &) -{ - if(sources.size()!=1) - throw invalid_argument("MsvcCompiler::create_target"); - SourceFile &source = dynamic_cast(*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).get_tag(); - - const FS::Path &vc_base_dir = ms_tools.get_vc_base_dir(); - tool.system_path.push_back(vc_base_dir/"include"); - - const FS::Path &win_sdk_dir = ms_tools.get_windows_sdk_dir(); - const string &win_sdk_ver = ms_tools.get_windows_sdk_version(); - tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"ucrt"); - tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"shared"); - tool.system_path.push_back(win_sdk_dir/"include"/win_sdk_ver/"um"); - - string path; - for(const FS::Path &p: tool.system_path) - { - append(path, ";", p.str()); - builder.get_logger().log("tools", "Got %s system path: %s", tool_tag, p); - } - - setenv("INCLUDE", path); -} - -ExternalTask::Arguments MsvcCompiler::_run(const ObjectFile &object, FS::Path &work_dir) -{ - const Tool &tool = *object.get_tool(); - - ExternalTask::Arguments argv; - argv.push_back(tool.get_executable()->get_path().str()); - argv.push_back("/nologo"); - argv.push_back("/c"); - - BuildInfo binfo; - object.collect_build_info(binfo); - - if(binfo.standards.count(tool.get_tag())) - { - const BuildInfo::LanguageStandard &std = get_item(binfo.standards, tool.get_tag()); - if((tool.get_tag()=="CXX" && std.year>2011) || (tool.get_tag()=="CC" && std.year>1999)) - argv.push_back("/std:"+std.str()); - } - - if(binfo.warning_level>=1) - { - argv.push_back(format("/W%d", binfo.warning_level)); - if(binfo.fatal_warnings) - argv.push_back("/WX"); - if(binfo.warning_level>=3) - argv.push_back("/permissive-"); - - argv.push_back("/wd4068"); // Unknown pragma - if(binfo.warning_level<4) - { - argv.push_back("/wd4244"); // Narrowing conversion on arg or return - argv.push_back("/wd4267"); // Narrowing conversion - } - } - else - argv.push_back("/w"); - - for(const FS::Path &p: binfo.local_incpath) - { - argv.push_back("/I"); - argv.push_back(p.str()); - } - for(const FS::Path &p: binfo.incpath) - { - argv.push_back("/I"); - argv.push_back(p.str()); - } - - for(const auto &kvp: binfo.defines) - { - argv.push_back("/D"); - if(kvp.second.empty()) - argv.push_back(kvp.first); - else - argv.push_back(format("%s=%s", kvp.first, kvp.second)); - } - - if(binfo.debug) - argv.push_back("/Z7"); - if(binfo.optimize) - { - if(binfo.optimize<0) - argv.push_back("/O1"); - else if(binfo.optimize==1) - argv.push_back("/Ox"); - else - argv.push_back("/O2"); - } - - if(binfo.libmode<=BuildInfo::STATIC) - argv.push_back(binfo.debug ? "/MTd" : "/MT"); - else - argv.push_back(binfo.debug ? "/MDd" : "/MD"); - - argv.push_back("/EHsc"); - - FS::Path obj_path = object.get_path(); - FS::Path src_path = object.get_source().get_path(); - - argv.push_back("/Fo"+relative(obj_path, work_dir).str()); - argv.push_back(relative(src_path, work_dir).str()); - - return argv; -} diff --git a/source/msvccompiler.h b/source/msvccompiler.h deleted file mode 100644 index 2782c23..0000000 --- a/source/msvccompiler.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MSVCCOMPILER_H_ -#define MSVCCOMPILER_H_ - -#include "tool.h" - -class MicrosoftTools; -class ObjectFile; - -class MsvcCompiler: public Tool -{ -private: - const MicrosoftTools &ms_tools; - -public: - MsvcCompiler(Builder &, const Architecture &, const std::string &, const MicrosoftTools &); - - Target *create_source(const Component &, const Msp::FS::Path &) const override; - Target *create_source(const Msp::FS::Path &) const override; - Target *create_target(const std::vector &, const std::string &) override; - std::string create_build_signature(const BuildInfo &) const override; - -protected: - void do_prepare(ToolData &) const override; - -public: - static ExternalTask::Arguments _run(const ObjectFile &, Msp::FS::Path &); -}; - -#endif diff --git a/source/msvclinker.cpp b/source/msvclinker.cpp deleted file mode 100644 index 9352c0f..0000000 --- a/source/msvclinker.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include -#include -#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 &sources, const string &arg) -{ - if(sources.empty()) - throw invalid_argument("MsvcLinker::create_target"); - - vector objs; - objs.reserve(sources.size()); - for(Target *s: sources) - objs.push_back(&dynamic_cast(*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).get_tag(); - const Architecture &arch = *static_cast(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 argv; - argv.push_back(tool.get_executable()->get_path().str()); - argv.push_back("/NOLOGO"); - - if(dynamic_cast(&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(d); - Target *tgt = d->get_real_target(); - - if(ObjectFile *obj = dynamic_cast(tgt)) - argv.push_back(relative(obj->get_path(), work_dir).str()); - else if(StaticLibrary *stlib = dynamic_cast(tgt)) - argv.push_back((file?file:stlib)->get_path().str()); - else if(ImportLibrary *imp = dynamic_cast(tgt)) - argv.push_back((file?file:imp)->get_path().str()); - } - - argv.push_back("/SUBSYSTEM:CONSOLE"); - - return argv; -} diff --git a/source/msvclinker.h b/source/msvclinker.h deleted file mode 100644 index 4320e48..0000000 --- a/source/msvclinker.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef MSVCLINKER_H_ -#define MSVCLINKER_H_ - -#include "tool.h" - -class Binary; -class MicrosoftTools; - -class MsvcLinker: public Tool -{ -private: - const MicrosoftTools &ms_tools; - -public: - MsvcLinker(Builder &, const Architecture &, const MicrosoftTools &); - - Target *create_target(const std::vector &, const std::string &) override; - std::string create_build_signature(const BuildInfo &) const override; - -protected: - void do_prepare(ToolData &data) const override; - -public: - static ExternalTask::Arguments _run(const Binary &, Msp::FS::Path &); -}; - -#endif diff --git a/source/objcsourcefile.cpp b/source/objcsourcefile.cpp deleted file mode 100644 index 953819f..0000000 --- a/source/objcsourcefile.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include "objcsourcefile.h" - -using namespace std; -using namespace Msp; - -void ObjCSourceFile::parse_includes(IO::Base &in) -{ - static Regex r_include("^[ \t]*#(include|import)[ \t]+([\"<].*)[\">]"); - - string line; - while(in.getline(line)) - if(RegMatch match = r_include.match(line)) - includes.push_back(match[2].str); -} diff --git a/source/objcsourcefile.h b/source/objcsourcefile.h deleted file mode 100644 index 37f79a3..0000000 --- a/source/objcsourcefile.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef OBJCSOURCEFILE_H_ -#define OBJCSOURCEFILE_H_ - -#include "csourcefile.h" - -/** -Represents an Objective-C source file. -*/ -class ObjCSourceFile: public CSourceFile -{ -public: - using CSourceFile::CSourceFile; - - const char *get_type() const override { return "ObjCSourceFile"; } - -protected: - void parse_includes(Msp::IO::Base &) override; -}; - -#endif diff --git a/source/objectfile.cpp b/source/objectfile.cpp deleted file mode 100644 index 00fa679..0000000 --- a/source/objectfile.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include -#include -#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(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 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(&ObjectFile::find_dependencies))); - } -} - -void ObjectFile::find_dependencies(FileTarget &tgt, vector &headers) -{ - tgt.prepare(); - - FileTarget *rtgt = dynamic_cast(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(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(d)) - { - auto i = lower_bound(headers, file); - if(i==headers.end() || *i!=file) - { - headers.insert(i, file); - find_dependencies(*file, headers); - } - } -} diff --git a/source/objectfile.h b/source/objectfile.h deleted file mode 100644 index 9e3ff90..0000000 --- a/source/objectfile.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef OBJECTFILE_H_ -#define OBJECTFILE_H_ - -#include "filetarget.h" - -class SourceFile; - -/** -Object files are compiled from source files. -*/ -class ObjectFile: public FileTarget -{ -private: - SourceFile &source; - bool used_in_shlib = false; - -public: - ObjectFile(Builder &, const Component &, SourceFile &); -private: - static Msp::FS::Path generate_target_path(const Component &, const Msp::FS::Path &); - -public: - const char *get_type() const override { return "ObjectFile"; } - SourceFile &get_source() const { return source; } - - void set_used_in_shared_library(bool); - bool is_used_in_shared_library() const { return used_in_shlib; } - - void collect_build_info(BuildInfo &) const override; - -private: - void find_dependencies() override; - - void find_dependencies(FileTarget &, std::vector &); -}; - -#endif diff --git a/source/package.cpp b/source/package.cpp deleted file mode 100644 index 10c2db0..0000000 --- a/source/package.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#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(p), - ArchitectureConditional(p.builder, p.name) -{ - add("label", &Package::label); - add("require", &Loader::require); -} - -void Package::Loader::require(const string &n) -{ - Package *req = obj.builder.get_package_manager().find_package(n); - if(req) - obj.requires.push_back(req); - else - obj.problems.push_back(format("Required package %s not found", n)); -} diff --git a/source/package.h b/source/package.h deleted file mode 100644 index d13aa73..0000000 --- a/source/package.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef PACKAGE_H_ -#define PACKAGE_H_ - -#include -#include -#include -#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, public ArchitectureConditional - { - public: - Loader(Package &); - private: - void require(const std::string &); - }; - - using Requirements = std::vector; - -protected: - Builder &builder; - - std::string name; - std::string label; - - Requirements requires; - BuildInfo export_binfo; - bool prepared = false; - std::vector 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 &get_problems() const { return problems; } - - virtual void save_caches() { } -}; - -#endif diff --git a/source/packagemanager.cpp b/source/packagemanager.cpp deleted file mode 100644 index 37374b6..0000000 --- a/source/packagemanager.cpp +++ /dev/null @@ -1,247 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 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 files = list_filtered(p, "\\.bpk$"); - for(const string &f: files) - binpkg_files.push_back(p/f); - builder.get_logger().log("packagemgr", "%d binary packages found in %s", files.size(), p); - } - - builder.get_logger().log("packagemgr", "%d binary packages found", binpkg_files.size()); - } - - auto i = find_if(binpkg_files, [&name](const FS::Path &p){ return FS::basepart(FS::basename(p))==name; }); - if(i!=binpkg_files.end()) - return *i; - - return FS::Path(); -} - -void PackageManager::save_all_caches() const -{ - for(const auto &kvp: packages) - kvp.second->save_caches(); -} diff --git a/source/packagemanager.h b/source/packagemanager.h deleted file mode 100644 index fc0aa98..0000000 --- a/source/packagemanager.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef PACKAGEMANAGER_H_ -#define PACKAGEMANAGER_H_ - -#include -#include -#include -#include -#include - -class Builder; -class Package; - -/** -Keeps track of packages. Also responsible for locating previously unknown -packages by name. -*/ -class PackageManager -{ -private: - Builder &builder; - std::vector pkg_path; - std::vector pkg_dirs; - std::vector binpkg_path; - std::vector binpkg_files; - bool no_externals = false; - std::map packages; - Package *main_pkg = 0; - std::set 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 &get_packages() const { return packages; } - - /** Locates a package and loads it if necessary. */ - Package *find_package(const std::string &); - -private: - std::string run_pkgconfig(const std::string &, const std::string &); - - /** Determines the source directory of a package. Pkg-config is consulted - first, and if it fails, the package path is searched for matches. The - package is expected to be located in a directory named after itself. */ - Msp::FS::Path get_package_location(const std::string &); - - /** Determines the file containing a binary package. The file is expected - to be named after the package. */ - Msp::FS::Path get_binary_package_file(const std::string &); - -public: - void save_all_caches() const; -}; - -#endif diff --git a/source/pattern.cpp b/source/pattern.cpp deleted file mode 100644 index fec83da..0000000 --- a/source/pattern.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include -#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 Pattern::apply_list(const vector &patterns, const string &body) -{ - vector result; - for(const Pattern &p: patterns) - result.push_back(p.apply(body)); - return result; -} diff --git a/source/pattern.h b/source/pattern.h deleted file mode 100644 index 8c623bd..0000000 --- a/source/pattern.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef PATTERN_H_ -#define PATTERN_H_ - -#include -#include - -/** -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 apply_list(const std::vector &, const std::string &); -}; - -#endif diff --git a/source/pkgconfigfile.cpp b/source/pkgconfigfile.cpp deleted file mode 100644 index 7631f9a..0000000 --- a/source/pkgconfigfile.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "builder.h" -#include "package.h" -#include "pkgconfigfile.h" - -PkgConfigFile::PkgConfigFile(Builder &b, const SourcePackage &p): - FileTarget(b, p, p.get_source_directory()/(p.get_name()+".pc")) -{ - tool = &builder.get_toolchain().get_tool("PCG"); - - install_location = "lib/pkgconfig"; -} diff --git a/source/pkgconfigfile.h b/source/pkgconfigfile.h deleted file mode 100644 index 81c8cd6..0000000 --- a/source/pkgconfigfile.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef PKGCONFIGFILE_H_ -#define PKGCONFIGFILE_H_ - -#include "sourcepackage.h" -#include "filetarget.h" - -/** -Creates a .pc file to enable other packages fetch build options with pkg-config. -*/ -class PkgConfigFile: public FileTarget -{ -public: - PkgConfigFile(Builder &, const SourcePackage &); - - const char *get_type() const override { return "PkgConfigFile"; } -}; - -#endif diff --git a/source/pkgconfiggenerator.cpp b/source/pkgconfiggenerator.cpp deleted file mode 100644 index 46387d3..0000000 --- a/source/pkgconfiggenerator.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include -#include -#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 &, const string &) -{ - throw logic_error("Not implemented"); -} - -bool PkgConfigGenerator::_run(const PkgConfigFile &pkgc) -{ - Builder &builder = pkgc.get_package()->get_builder(); - const SourcePackage &spkg = *pkgc.get_package(); - - IO::BufferedFile out(pkgc.get_path().str(), IO::M_WRITE); - IO::print(out, "prefix=%s\n", builder.get_prefix().str()); - IO::print(out, "source=%s\n\n", spkg.get_source_directory()); - - IO::print(out, "Name: %s\n", spkg.get_label()); - IO::print(out, "Description: %s\n", spkg.get_description()); - IO::print(out, "Version: %s\n", spkg.get_version()); - - IO::print(out, "Requires:"); - for(const Package *r: spkg.get_required_packages()) - if(r->uses_pkgconfig()) - IO::print(out, " %s", r->get_name()); - out.put('\n'); - - const BuildInfo &binfo = spkg.get_exported_build_info(); - IO::print(out, "Libs:"); - for(const FS::Path &p: binfo.libpath) - IO::print(out, " -L%s", prefixify(p, builder.get_prefix())); - for(const string &l: binfo.libs) - IO::print(out, " -l%s", l); - if(binfo.threads) - out.write("-pthread"); - out.put('\n'); - - IO::print(out, "Cflags:"); - for(const FS::Path &p: binfo.incpath) - IO::print(out, " -I%s", prefixify(p, builder.get_prefix())); - for(const auto &kvp: binfo.defines) - if(kvp.second.empty()) - IO::print(out, " -D%s", kvp.first); - else - IO::print(out, " -D%s=%s", kvp.first, kvp.second); - out.put('\n'); - - return true; -} - -string PkgConfigGenerator::prefixify(const FS::Path &path, const FS::Path &prefix) -{ - if(FS::descendant_depth(path, prefix)>=0) - { - FS::Path rel_path = FS::relative(path, prefix); - return "${prefix}"+rel_path.str().substr(1); - } - else - return path.str(); -} diff --git a/source/pkgconfiggenerator.h b/source/pkgconfiggenerator.h deleted file mode 100644 index 761a2c0..0000000 --- a/source/pkgconfiggenerator.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef PKGCONFIGGENERATOR_H_ -#define PKGCONFIGGENERATOR_H_ - -#include "tool.h" - -class PkgConfigFile; - -class PkgConfigGenerator: public Tool -{ -public: - PkgConfigGenerator(Builder &); - - Target *create_target(const std::vector &, const std::string &) override; - -private: - static bool _run(const PkgConfigFile &); - static std::string prefixify(const Msp::FS::Path &, const Msp::FS::Path &); -}; - -#endif diff --git a/source/sharedlibrary.cpp b/source/sharedlibrary.cpp deleted file mode 100644 index 0673a05..0000000 --- a/source/sharedlibrary.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#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 &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(*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(format("%s-%s", libname, version)); - else if(arch.get_system()=="darwin") - soname = arch.create_filename(format("%s.%s", libname, version)); - else - soname = format("%s.%s", arch.create_filename(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(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(comp.get_name()); - } -} - -void SharedLibrary::set_import_library(ImportLibrary *imp) -{ - import_lib = imp; -} diff --git a/source/sharedlibrary.h b/source/sharedlibrary.h deleted file mode 100644 index 564b210..0000000 --- a/source/sharedlibrary.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef SHAREDLIB_H_ -#define SHAREDLIB_H_ - -#include "binary.h" - -class ImportLibrary; - -/** -Represents a shared library. It has two special properties: libname and -soname. Libname is the name used by the linker. Soname is the canonical -filename of the library, including version number. If the owning package has -no version, soname will be empty. - -A SharedLibrary can also store a pointer to the associated ImportLibrary, for -platforms that need one. -*/ -class SharedLibrary: public Binary -{ -private: - std::string libname; - std::string soname; - ImportLibrary *import_lib = 0; - -public: - SharedLibrary(Builder &, const Msp::FS::Path &); - SharedLibrary(Builder &, const Component &, const std::vector &); -private: - static std::string generate_filename(const Component &); - -public: - const char *get_type() const override { return "SharedLibrary"; } - const std::string &get_libname() const { return libname; } - const std::string &get_soname() const { return soname; } - - void set_import_library(ImportLibrary *); - ImportLibrary *get_import_library() const { return import_lib; } -}; - -#endif diff --git a/source/sourcearchivecomponent.cpp b/source/sourcearchivecomponent.cpp deleted file mode 100644 index b1e4279..0000000 --- a/source/sourcearchivecomponent.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#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 files; - files.push_back(&package.get_build_file()); - - for(const FS::Path &s: collect_source_files()) - { - FileTarget *file = builder.get_vfs().get_target(s); - if(!file) - file = new File(builder, package, s); - files.push_back(file); - } - - BuildGraph &build_graph = builder.get_build_graph(); - for(const auto &kvp: build_graph.get_targets()) - if(kvp.second->get_package()==&package && !kvp.second->is_buildable()) - if(!any_equals(files, kvp.second)) - files.push_back(kvp.second); - - const Toolchain &toolchain = builder.get_toolchain(); - string archive_name = package.get_name(); - if(!package.get_version().empty()) - archive_name += "-"+package.get_version(); - archive_name += "-source"; - Target *result = toolchain.get_tool("TAR").create_target(files, archive_name); - build_graph.get_target("archives")->add_dependency(*result); -} diff --git a/source/sourcearchivecomponent.h b/source/sourcearchivecomponent.h deleted file mode 100644 index 94c1bc5..0000000 --- a/source/sourcearchivecomponent.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef TARBALLCOMPONENT_H_ -#define TARBALLCOMPONENT_H_ - -#include "component.h" - -class SourceArchiveComponent: public Component -{ -public: - SourceArchiveComponent(SourcePackage &); - - void create_targets() const override; -}; - -#endif diff --git a/source/sourcefile.cpp b/source/sourcefile.cpp deleted file mode 100644 index b5b84c7..0000000 --- a/source/sourcefile.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "component.h" -#include "sourcefile.h" -#include "sourcepackage.h" - -SourceFile::SourceFile(Builder &b, const Component &c, const Msp::FS::Path &p): - FileTarget(b, c.get_package(), p) -{ - component = &c; -} diff --git a/source/sourcefile.h b/source/sourcefile.h deleted file mode 100644 index 16521a5..0000000 --- a/source/sourcefile.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SOURCEFILE_H_ -#define SOURCEFILE_H_ - -#include "filetarget.h" - -class SourceFile: public FileTarget -{ -protected: - SourceFile(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { } - SourceFile(Builder &, const Component &, const Msp::FS::Path &); -}; - -#endif diff --git a/source/sourcegenerator.cpp b/source/sourcegenerator.cpp deleted file mode 100644 index d30a2bc..0000000 --- a/source/sourcegenerator.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#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 &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(*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(*out_src.get_tool()); - - vector 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(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(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(&SourceGenerator::processing_unit)); -} - -void SourceGenerator::Loader::argument(const string &a) -{ - obj.arguments.push_back(a); -} - -void SourceGenerator::Loader::arguments(const vector &a) -{ - obj.arguments.insert(obj.arguments.end(), a.begin(), a.end()); -} - -void SourceGenerator::Loader::command(const string &c) -{ - if(c.find('/')!=string::npos) - obj.set_command((obj.package.get_source_directory()/c).str()); - else - obj.set_command(c); -} - -void SourceGenerator::Loader::in_suffix(const string &s) -{ - obj.input_suffixes.push_back(s); -} - -void SourceGenerator::Loader::out_suffix(const string &s) -{ - obj.out_suffixes.push_back(s); -} diff --git a/source/sourcegenerator.h b/source/sourcegenerator.h deleted file mode 100644 index b8dee24..0000000 --- a/source/sourcegenerator.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef SOURCEGENERATOR_H_ -#define SOURCEGENERATOR_H_ - -#include -#include "conditionalloader.h" -#include "sourcepackage.h" -#include "tool.h" - -class SourceFile; - -class SourceGenerator: public Tool -{ -public: - class Loader: public Msp::DataFile::ObjectLoader, public ConditionalLoader - { - public: - Loader(SourceGenerator &); - - private: - void argument(const std::string &); - void arguments(const std::vector &); - 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 out_suffixes; - std::vector 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 &, const std::string &) override; - -private: - static ExternalTask::Arguments _run(const SourceFile &, Msp::FS::Path &); -}; - -#endif diff --git a/source/sourcepackage.cpp b/source/sourcepackage.cpp deleted file mode 100644 index 2c478a8..0000000 --- a/source/sourcepackage.cpp +++ /dev/null @@ -1,273 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#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(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(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(p), - FeatureConditional(p, p.name), - options(o) -{ - add("android_application", &Loader::component); - add("build_info", &Loader::build_info); - add("datapack", &Loader::component); - add("description", &SourcePackage::description); - add("feature", &Loader::feature); - add("generate", &Loader::generate); - add("install", &Loader::component); - add("interface_version", &Loader::interface_version); - add("library", &Loader::component_arg, BinaryComponent::LIBRARY); - add("module", &Loader::component_arg, BinaryComponent::MODULE); - add("program", &Loader::component_arg, 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 -void SourcePackage::Loader::component(const string &n) -{ - C *comp = new C(obj, n); - load_sub(*comp); - obj.components.push_back(comp); -} - -template -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=2) - break; - obj.interface_version = obj.version.substr(0, i); -} diff --git a/source/sourcepackage.h b/source/sourcepackage.h deleted file mode 100644 index 8be8ada..0000000 --- a/source/sourcepackage.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef SOURCEPACKAGE_H_ -#define SOURCEPACKAGE_H_ - -#include -#include -#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, 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 - void component(const std::string &); - template - 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 features; - BuildInfo build_info; - std::vector components; - SourceArchiveComponent *source_archive; - Config config; - mutable Cache cache; - -public: - SourcePackage(Builder &, const std::string &, const Msp::FS::Path &); - ~SourcePackage(); - - const std::string &get_version() const { return version; } - const std::string &get_interface_version() const { return interface_version; } - const std::string &get_description() const { return description; } - - FileTarget &get_build_file() const { return *build_file; } - const Msp::FS::Path &get_source_directory() const { return source_dir; } - Msp::FS::Path get_temp_directory() const; - Msp::FS::Path get_output_directory() const; - - const Toolchain &get_toolchain() const { return local_tools; } - const Component &get_component(const std::string &) const; - const Config &get_config() const { return config; } - bool match_feature(const std::string &, const std::string *) const; - void set_build_type(const BuildType &); - const BuildInfo &get_build_info() const { return build_info; } -private: - void do_prepare() override; - -public: - Cache &get_cache() const { return cache; } -private: - void save_caches() override; -}; - -#endif diff --git a/source/staticlibrary.cpp b/source/staticlibrary.cpp deleted file mode 100644 index 039eb8e..0000000 --- a/source/staticlibrary.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "builder.h" -#include "component.h" -#include "objectfile.h" -#include "sourcepackage.h" -#include "staticlibrary.h" - -using namespace std; -using namespace Msp; - -StaticLibrary::StaticLibrary(Builder &b, const Component &c, const vector &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(comp.get_name()); -} - -void StaticLibrary::add_required_library(const string &lib) -{ - build_info.libs.push_back(lib); -} - -void StaticLibrary::add_library_path(const FS::Path &pth) -{ - build_info.libpath.push_back(pth); -} - -void StaticLibrary::collect_build_info(BuildInfo &binfo) const -{ - Target::collect_build_info(binfo); - binfo.update_from(build_info); -} diff --git a/source/staticlibrary.h b/source/staticlibrary.h deleted file mode 100644 index 98d2990..0000000 --- a/source/staticlibrary.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef STATICLIBRARY_H_ -#define STATICLIBRARY_H_ - -#include "filetarget.h" - -class Component; -class ObjectFile; - -/** -A static library target. -*/ -class StaticLibrary: public FileTarget -{ -private: - /* TODO this really belongs in a Component, but some refactoring is required - to allow non-builder packages to have components. Rename BinaryPackage to - ExternalPackage, add BuildableComponent and ExternalComponent classes. */ - BuildInfo build_info; - -public: - StaticLibrary(Builder &b, const Msp::FS::Path &p): FileTarget(b, p) { } - StaticLibrary(Builder &, const Component &, const std::vector &); -private: - static std::string generate_filename(const Component &); - -public: - const char *get_type() const override { return "StaticLibrary"; } - - void add_required_library(const std::string &); - void add_library_path(const Msp::FS::Path &); - void collect_build_info(BuildInfo &) const override; -}; - -#endif diff --git a/source/sysutils.cpp b/source/sysutils.cpp deleted file mode 100644 index 95867b7..0000000 --- a/source/sysutils.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#define _WIN32_WINNT _WIN32_WINNT_VISTA -#define WIN32_LEAN_AND_MEAN -#define INITGUID -#ifdef _WIN32 -#include -#include -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#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( - string(reinterpret_cast(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(const string &path) -{ -#ifdef _WIN32 - string::size_type first_sep = path.find('\\'); - string::size_type last_sep = path.rfind('\\'); - string root = path.substr(0, first_sep); - string key_path = path.substr(first_sep+1, last_sep-first_sep-1); - string value_name = path.substr(last_sep+1); - - HKEY root_handle; - if(root=="HKCR") - root_handle = HKEY_CLASSES_ROOT; - else if(root=="HKCC") - root_handle = HKEY_CURRENT_CONFIG; - else if(root=="HKCU") - root_handle = HKEY_CURRENT_USER; - else if(root=="HKLM") - root_handle = HKEY_LOCAL_MACHINE; - else if(root=="HKU") - root_handle = HKEY_USERS; - else - throw invalid_argument("get_registry_value"); - - HKEY key; - LSTATUS err = RegOpenKeyEx(root_handle, key_path.c_str(), 0, KEY_READ, &key); - if(err!=ERROR_SUCCESS) - throw Msp::system_error("RegOpenKey", err); - - DWORD value_len; - err = RegGetValue(key, 0, value_name.c_str(), RRF_RT_REG_SZ, 0, 0, &value_len); - if(err!=ERROR_SUCCESS) - throw Msp::system_error("RegGetValue", err); - - char *buffer = new char[value_len]; - err = RegGetValue(key, 0, value_name.c_str(), RRF_RT_REG_SZ, 0, buffer, &value_len); - if(err!=ERROR_SUCCESS) - { - delete[] buffer; - throw Msp::system_error("RegGetValue", err); - } - - string result(buffer); - delete[] buffer; - return result; -#else - (void)path; - return string(); -#endif -} diff --git a/source/sysutils.h b/source/sysutils.h deleted file mode 100644 index 7300869..0000000 --- a/source/sysutils.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SYSUTILS_H_ -#define SYSUTILS_H_ - -#include -#include - -std::string get_system_type(); -Msp::FS::Path get_program_files_x86_dir(); - -template -T get_registry_value(const std::string &); - -#endif diff --git a/source/tar.cpp b/source/tar.cpp deleted file mode 100644 index 07c14ce..0000000 --- a/source/tar.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include -#include -#include -#include -#include -#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 &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(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(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 &, const std::string &) override; - -private: - static bool _run(const TarBall &); - static void store_number(char *, unsigned, unsigned); -}; - -#endif diff --git a/source/tarball.cpp b/source/tarball.cpp deleted file mode 100644 index 7596a3d..0000000 --- a/source/tarball.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "sourcepackage.h" -#include "tar.h" -#include "tarball.h" - -using namespace std; - -TarBall::TarBall(Builder &b, const SourcePackage &p, const string &n): - FileTarget(b, p, p.get_source_directory()/(n+".tar")) -{ } - -const SourcePackage *TarBall::get_package() const -{ - return static_cast(package); -} diff --git a/source/tarball.h b/source/tarball.h deleted file mode 100644 index 105a2b1..0000000 --- a/source/tarball.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef TARBALL_H_ -#define TARBALL_H_ - -#include "filetarget.h" - -class TarBall: public FileTarget -{ -public: - TarBall(Builder &, const SourcePackage &, const std::string &); - - const char *get_type() const override { return "TarBall"; } - const SourcePackage *get_package() const; -}; - -#endif diff --git a/source/target.cpp b/source/target.cpp deleted file mode 100644 index dbdd950..0000000 --- a/source/target.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include -#include -#include -#include "builder.h" -#include "filetarget.h" -#include "sourcepackage.h" -#include "target.h" -#include "task.h" -#include "tool.h" - -using namespace std; -using namespace Msp; - -Target::Target(Builder &b, const string &n): - builder(b), - name(n) -{ - builder.get_build_graph().add_target(this); -} - -void Target::add_dependency(Target &dep) -{ - if(&dep==this) - throw invalid_argument("Target::add_depend"); - depends.push_back(&dep); - if(state>PREPARING) - dep.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild)); -} - -void Target::add_transitive_dependency(Target &dep) -{ - if(&dep==this) - throw invalid_argument("Target::add_transitive_dependency"); - trans_depends.push_back(&dep); -} - -void Target::add_side_effect(Target &se) -{ - side_effects.push_back(&se); - if(tool) - se.set_tool(*tool); - se.primary_target = this; - /* Side effects are checked for rebuild after the primary target. Recheck - the primary if a side effect is marked for rebuild. */ - se.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild)); -} - -Target *Target::get_buildable_target() -{ - if(primary_target) - return primary_target->get_buildable_target(); - if(!needs_rebuild()) - return 0; - - bool self_ok = state!=BUILDING; - for(Target *d: depends) - { - // Avoid infinite recursion if a target depends on its own side effect - if(any_equals(side_effects, d)) - continue; - - Target *tgt = d->get_buildable_target(); - if(tgt) - return tgt; - else if(d->needs_rebuild()) - self_ok = false; - } - - if(self_ok) - return this; - - return 0; -} - -void Target::set_tool(Tool &t) -{ - tool = &t; - for(Target *s: side_effects) - s->set_tool(t); -} - -void Target::collect_build_info(BuildInfo &binfo) const -{ - if(tool) - binfo.update_from(tool->get_build_info()); - if(component) - binfo.update_from(component->get_build_info()); - else if(package) - binfo.update_from(package->get_build_info()); -} - -void Target::force_rebuild() -{ - if(!is_buildable()) - throw logic_error("Target::force_rebuild"); - mark_rebuild("Forced rebuild"); -} - -void Target::mark_rebuild(const string &reason) -{ - if(reason.empty()) - throw invalid_argument("No reason given for rebuilding "+name); - - state = REBUILD; - rebuild_reason = reason; - - builder.get_logger().log("rebuild", "Rebuilding %s: %s", name, reason); - - signal_bubble_rebuild.emit(); -} - -void Target::prepare() -{ - if(state>PREPARING) - return; - if(state==PREPARING) - { - builder.get_logger().log("problems", "Dependency cycle detected at %s", name); - problems.push_back("Dependency cycle detected"); - state = BROKEN; - return; - } - - state = PREPARING; - /* Prepare existing dependencies early, because their information may be - needed to find other dependencies. */ - for(Target *d: depends) - d->prepare(); - if(tool) - tool->prepare(); - - find_dependencies(); - bool broken = !problems.empty(); - - if(tool) - { - if(FileTarget *tool_exe = tool->get_executable()) - add_dependency(*tool_exe); - broken |= !tool->get_problems().empty(); - - // Only check package and component problems for buildable targets - // XXX How to propagate nested package problems? - broken |= (package && !package->get_problems().empty()); - broken |= (component && !component->get_problems().empty()); - } - - /* Now that all dependencies are known, prepare them again. This will do - nothing to already prepared targets. */ - for(Target *d: depends) - { - d->prepare(); - broken |= d->is_broken(); - } - for(Target *d: trans_depends) - d->prepare(); - - check_rebuild(); - if(broken) - state = BROKEN; - else if(state==PREPARING) - state = UPTODATE; - - for(Target *d: depends) - d->signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild)); -} - -Task *Target::build() -{ - if(primary_target) - return primary_target->build(); - - Task *task = tool->run(*this); - task->signal_finished.connect(sigc::mem_fun(this, &Target::build_finished)); - state = BUILDING; - - build(*task); - for(Target *s: side_effects) - s->build(*task); - - return task; -} - -void Target::build_finished(bool success) -{ - state = UPTODATE; - if(success) - { - modified(); - for(Target *s: side_effects) - s->build_finished(success); - signal_modified.emit(); - } -} diff --git a/source/target.h b/source/target.h deleted file mode 100644 index 6ef6457..0000000 --- a/source/target.h +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef TARGET_H_ -#define TARGET_H_ - -#include -#include -#include -#include -#include -#include - -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; - -protected: - enum State - { - INIT, - PREPARING, - REBUILD, - BUILDING, - UPTODATE, - BROKEN - }; - -public: - sigc::signal signal_bubble_rebuild; - sigc::signal 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 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 &get_problems() const { return problems; } - - /** Prepares the target by finding dependencies, recursively preparing them - and then checking whether rebuilding is needed. */ - void prepare(); - - /** Invokes the associated Tool to build the target and returns the - resulting Task. The task must be started by the caller. */ - virtual Task *build(); - -protected: - /** Targets can override this to do additional setup on the Task. This is - also called on side effects, which normally do not get built by themselves. */ - virtual void build(Task &) { } - - /** Handler for Task::signal_finished. */ - virtual void build_finished(bool); - - virtual void modified() { } - -public: - /** Removes any results of building the target. */ - virtual void clean() { } -}; - -#endif diff --git a/source/task.cpp b/source/task.cpp deleted file mode 100644 index e72a741..0000000 --- a/source/task.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -#include -#include "task.h" - -using namespace std; -using namespace Msp; - -void Task::add_file(const FS::Path &f) -{ - files.push_back(f); -} - -void Task::set_unlink(bool u) -{ - unlink = u; -} - -void Task::prepare() -{ - for(const FS::Path &f: files) - { - if(FS::exists(f)) - { - // If the file exists, the directory it's in must exist too - FS::unlink(f); - } - else - { - FS::Path dir = FS::dirname(f); - if(!FS::exists(dir)) - FS::mkpath(dir, 0755); - } - } -} diff --git a/source/task.h b/source/task.h deleted file mode 100644 index 915ffe1..0000000 --- a/source/task.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef TASK_H_ -#define TASK_H_ - -#include -#include -#include - -/** -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 signal_finished; - -protected: - std::vector files; - bool unlink = false; - - Task() = default; -public: - virtual ~Task() { } - - /** Associate the task with a file. */ - void add_file(const Msp::FS::Path &); - - /** If set to true, the associated files are removed before the task is - started. */ - void set_unlink(bool = true); - - /** Returns the command being executed for this task. Only makes sense if - an external command is involved. */ - virtual std::string get_command() const = 0; - - /// Starts the task. - virtual void start() = 0; - -protected: - /// Ensures that the output directory exists and removes files if necessary. - void prepare(); - -public: - /// Checks the status of the task and immediately returns. - virtual Status check() = 0; - - /// Waits for the task to finish and returns its final status. - virtual Status wait() = 0; -}; - -#endif diff --git a/source/templatefile.h b/source/templatefile.h deleted file mode 100644 index 0912025..0000000 --- a/source/templatefile.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef TEMPLATEFILE_H_ -#define TEMPLATEFILE_H_ - -#include "sourcefile.h" - -/** -Input file for SourceGenerator. -*/ -class TemplateFile: public SourceFile -{ -public: - TemplateFile(Builder &b, const Component &c, const Msp::FS::Path &p): SourceFile(b, c, p) { } - - const char *get_type() const override { return "TemplateFile"; } -}; - -#endif diff --git a/source/tool.cpp b/source/tool.cpp deleted file mode 100644 index eaf4977..0000000 --- a/source/tool.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include -#include -#include -#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 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 sources; - sources.push_back(&source); - return create_target(sources, arg); -} - -void Tool::prepare() -{ - if(prepared) - return; - - prepared = true; - - if(!command.empty()) - executable = builder.get_vfs().find_binary(command); - prepare(*this); - if(!command.empty() && !executable) - { - builder.get_logger().log("problems", "Can't find executable %s for %s", command, tag); - problems.push_back(format("Can't find executable %s", command)); - } -} - -void Tool::prepare(Tool &tool) const -{ - if(&tool!=this && tool.get_base_tool()!=this) - throw invalid_argument("Tool::prepare"); - - if(&tool!=this && !command.empty() && tool.command.empty()) - throw logic_error("Derived tool has no command"); - - do_prepare(tool); -} - -string Tool::create_build_signature(const BuildInfo &) const -{ - if(executable) - return format("%s=%s", tag, FS::basename(executable->get_path())); - else - return string(); -} - - -void operator>>(const LexicalConverter &conv, Tool::ProcessingUnit &unit) -{ - const string &str = conv.get(); - if(str=="FILE") - unit = Tool::ONE_FILE; - else if(str=="DIRECTORY") - unit = Tool::DIRECTORY; - else if(str=="COMPONENT") - unit = Tool::COMPONENT; - else - throw lexical_error(format("conversion of '%s' to ProcessingUnit", str)); -} diff --git a/source/tool.h b/source/tool.h deleted file mode 100644 index b756563..0000000 --- a/source/tool.h +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef TOOL_H_ -#define TOOL_H_ - -#include -#include -#include -#include -#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 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 input_suffixes; - std::vector aux_suffixes; - ProcessingUnit processing_unit = ONE_FILE; - std::function 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); - - template - void set_run_external(ExternalTask::Arguments (*)(const T &, Msp::FS::Path &)); - - template - 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 &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 &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 &, 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 &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 -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(t), work_dir); - return new ExternalTask(args, work_dir); - }); -} - -template -void Tool::set_run_internal(bool (*f)(const T &)) -{ - set_run([f](const Target &t){ - const T &ct = dynamic_cast(t); - return new InternalTask([f, &ct]{ return f(ct); }); - }); -} - - -void operator>>(const Msp::LexicalConverter &, Tool::ProcessingUnit &); - -#endif diff --git a/source/toolchain.cpp b/source/toolchain.cpp deleted file mode 100644 index c5b8caa..0000000 --- a/source/toolchain.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include "tool.h" -#include "toolchain.h" - -using namespace std; -using namespace Msp; - -Toolchain::~Toolchain() -{ - for(const auto &kvp: tools) - delete kvp.second; - for(Toolchain *c: chains) - delete c; -} - -void Toolchain::add_tool(Tool *tool) -{ - insert_unique(tools, tool->get_tag(), tool); -} - -void Toolchain::add_toolchain(Toolchain *chain) -{ - auto i = upper_bound(chains, chain->get_priority(), [](int p, Toolchain *tc){ return p>tc->get_priority(); }); - chains.insert(i, chain); -} - -bool Toolchain::has_tool(const string &tag) const -{ - if(tools.count(tag)) - return true; - return any_of(chains.begin(), chains.end(), [&tag](Toolchain *tc){ return tc->has_tool(tag); }); -} - -Tool &Toolchain::get_tool(const string &tag) const -{ - if(!tools.count(tag)) - { - for(const Toolchain *c: chains) - if(c->has_tool(tag)) - return c->get_tool(tag); - } - - return *get_item(tools, tag); -} - -Tool *Toolchain::get_tool_for_suffix(const string &suffix, bool aux) const -{ - for(const auto &kvp: tools) - if(kvp.second->accepts_suffix(suffix, aux)) - return kvp.second; - - for(const Toolchain *c: chains) - if(Tool *tool = c->get_tool_for_suffix(suffix, aux)) - return tool; - - return 0; -} diff --git a/source/toolchain.h b/source/toolchain.h deleted file mode 100644 index 34fc0a2..0000000 --- a/source/toolchain.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef TOOLCHAIN_H_ -#define TOOLCHAIN_H_ - -#include -#include -#include - -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 tools; - std::vector 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 &get_toolchains() { return chains; } -}; - -#endif diff --git a/source/vcxprojectfile.cpp b/source/vcxprojectfile.cpp deleted file mode 100644 index c591524..0000000 --- a/source/vcxprojectfile.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#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(digest[j])); - } -} diff --git a/source/vcxprojectfile.h b/source/vcxprojectfile.h deleted file mode 100644 index 268d4bb..0000000 --- a/source/vcxprojectfile.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef VCXPROJECTFILE_H_ -#define VCXPROJECTFILE_H_ - -#include "filetarget.h" - -class VcxProjectFile: public FileTarget -{ -private: - std::string guid; - -public: - VcxProjectFile(Builder &, const SourcePackage &); - - const char *get_type() const override { return "VcxProjectFile"; } - - const std::string &get_guid() const { return guid; } -}; - -#endif diff --git a/source/vcxprojectgenerator.cpp b/source/vcxprojectgenerator.cpp deleted file mode 100644 index 8cfc12a..0000000 --- a/source/vcxprojectgenerator.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include -#include -#include -#include -#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 &, 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, "\n"); - - IO::print(out, "\t\n"); - vector 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\n", b, p); - IO::print(out, "\t\t\t%s\n", b); - IO::print(out, "\t\t\t%s\n", p); - IO::print(out, "\t\t\n"); - } - IO::print(out, "\t\n"); - - IO::print(out, "\t\n"); - IO::print(out, "\t\t15.0\n"); - IO::print(out, "\t\tMakeFileProj\n"); - IO::print(out, "\t\t{%s}\n", project.get_guid()); - IO::print(out, "\t\n"); - - IO::print(out, "\t\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(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\n", b, p); - IO::print(out, "\t\tMakeFile\n"); - IO::print(out, "\t\t%s\n", base_cmd); - IO::print(out, "\t\t%s -c\n", base_cmd); - IO::print(out, "\t\t%s -B\n", base_cmd); - if(exe) - IO::print(out, "\t\t%s\n", exe->get_path()); - IO::print(out, "\t\n"); - } - - IO::print(out, "\t\n"); - - vector sources; - vector includes; - vector 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(kvp.second)) - { - if(dynamic_cast(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\n"); - string path_str; - for(const FS::Path &p: build_info.incpath) - append(path_str, ";", p.str()); - IO::print(out, "\t\t%s\n", path_str); - IO::print(out, "\t\n"); - } - - IO::print(out, "\t\n"); - for(const FileTarget *s: sources) - IO::print(out, "\t\t\n", s->get_path()); - IO::print(out, "\t\n"); - - IO::print(out, "\t\n"); - for(const FileTarget *i: includes) - IO::print(out, "\t\t\n", i->get_path()); - IO::print(out, "\t\n"); - - IO::print(out, "\t\n"); - for(const FileTarget *t: others) - IO::print(out, "\t\t\n", t->get_path()); - IO::print(out, "\t\n"); - - IO::print(out, "\t\n"); - IO::print(out, "\n"); - - return true; -} diff --git a/source/vcxprojectgenerator.h b/source/vcxprojectgenerator.h deleted file mode 100644 index f8283ff..0000000 --- a/source/vcxprojectgenerator.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef VCXPROJECTGENERATOR_H_ -#define VCXPROJECTGENERATOR_H_ - -#include "tool.h" - -class VcxProjectFile; - -class VcxProjectGenerator: public Tool -{ -public: - VcxProjectGenerator(Builder &); - - Target *create_target(const std::vector &, const std::string &) override; - -private: - static bool _run(const VcxProjectFile &); -}; - -#endif diff --git a/source/virtualfilesystem.cpp b/source/virtualfilesystem.cpp deleted file mode 100644 index 16d4de8..0000000 --- a/source/virtualfilesystem.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include -#include -#include -#include -#include -#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(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(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 shared_names; - bool use_import_lib = false; - if(mode!=BuildInfo::FORCE_STATIC) - { - shared_names = Pattern::apply_list(arch.get_patterns(), lib); - if(!(use_import_lib = !shared_names.empty())) - shared_names = Pattern::apply_list(arch.get_patterns(), lib); - } - - vector static_names; - if(mode!=BuildInfo::FORCE_DYNAMIC) - static_names = Pattern::apply_list(arch.get_patterns(), lib); - - for(const FS::Path &p: combined_path) - { - const vector *cur_names = (mode>=BuildInfo::DYNAMIC ? &shared_names : &static_names); - for(auto j=cur_names->begin(); j!=cur_names->end(); ) - { - FS::Path filename = p / *j; - if(FileTarget *tgt = get_target(filename)) - { - builder.get_logger().log("vfs", "Library %s (%s) found in %s as existing %s", lib, *j, p.str(), tgt->get_type()); - return tgt; - } - else if(file_exists(filename)) - { - builder.get_logger().log("vfs", "Library %s (%s) found in %s", lib, *j, p.str()); - if(cur_names==&shared_names) - { - if(use_import_lib) - return new ImportLibrary(builder, filename); - return new SharedLibrary(builder, filename); - } - else - return new StaticLibrary(builder, filename); - } - - if(++j==cur_names->end()) - { - if(mode==BuildInfo::DYNAMIC && cur_names==&shared_names) - cur_names = &static_names; - else if(mode==BuildInfo::STATIC && cur_names==&static_names) - cur_names = &shared_names; - else - break; - j = cur_names->begin(); - } - } - - builder.get_logger().log("vfs", "Library %s not found in %s", lib, p.str()); - } - - return 0; -} - -FileTarget *VirtualFileSystem::find_binary(const string &name) -{ - SearchPath path; - if(FS::Path(name).is_absolute()) - path.push_back("/"); - else - { - if(sys_bin_path.empty()) - { - string env_path = Msp::getenv("PATH"); - if(!env_path.empty()) - { - for(const string &p: split(env_path, ':')) - sys_bin_path.push_back(p); - } - else - { - sys_bin_path.push_back("/bin"); - sys_bin_path.push_back("/usr/bin"); - } - } - path = sys_bin_path; - } - - for(const FS::Path &p: path) - { - FS::Path filename = p/name; - if(FileTarget *tgt = get_target(filename)) - { - builder.get_logger().log("vfs", "Binary %s found in %s as existing %s", name, p, tgt->get_type()); - return tgt; - } - else if(file_exists(filename)) - { - builder.get_logger().log("vfs", "Binary %s found in %s", name, p); - return new Executable(builder, filename); - } - - builder.get_logger().log("vfs", "Binary %s not found in %s", name, p); - } - - return 0; -} - -bool VirtualFileSystem::file_exists(const FS::Path &filename) -{ - if(nonexistent.count(filename)) - return false; - if(FS::is_reg(filename)) - return true; - nonexistent.insert(filename); - return false; -} diff --git a/source/virtualfilesystem.h b/source/virtualfilesystem.h deleted file mode 100644 index 60e198a..0000000 --- a/source/virtualfilesystem.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef VIRTUALFILESYSTEM_H_ -#define VIRTUALFILESYSTEM_H_ - -#include -#include -#include -#include -#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; - -private: - Builder &builder; - std::map targets; - std::set nonexistent; - SearchPath sys_bin_path; - -public: - VirtualFileSystem(Builder &b): builder(b) { } - - /** Gets an existing target associated with a path. If no target has claimed - that path, 0 is returned. */ - FileTarget *get_target(const Msp::FS::Path &) const; - - /** Registers a target with the VFS. A target may be registered at multiple - paths if building it results in multiple files. */ - void register_path(const Msp::FS::Path &, FileTarget *); - - /** Locates a source file. If a file is found but no target is associated - with it, a new package-less target is created with the appropriate tool. If - use_syspath is true, the system path reported by the tool is also searched. */ - FileTarget *find_header(const std::string &, Tool *, const SearchPath &, bool use_syspath = true); - - /** Locates a library. The library name should be the same as what would be - used in linking with the library. If a file is found but no target is - associated with it, a new package-less target of appropriate type is - created. If use_syspath is true, the system path reported by the LINK tool - is also searched. */ - FileTarget *find_library(const std::string &, const SearchPath &, BuildInfo::LibraryMode, bool use_syspath = true); - - /** Locates a binary. The normal search path for binaries is used (usually - this means the PATH environment variable). If a file is found but no target - is associated with it, a new package-less Executable target is created. */ - FileTarget *find_binary(const std::string &); - -private: - bool file_exists(const Msp::FS::Path &); -}; - -#endif diff --git a/source/virtualtarget.cpp b/source/virtualtarget.cpp deleted file mode 100644 index 07177d8..0000000 --- a/source/virtualtarget.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include -#include -#include "builder.h" -#include "virtualtarget.h" - -using namespace std; -using namespace Msp; - -void VirtualTarget::check_rebuild() -{ - // Virtual targets are only rebuilt if their dependencies need rebuilding. - auto i = find_if(depends, [](Target *d){ return d->needs_rebuild(); }); - if(i!=depends.end()) - mark_rebuild((*i)->get_name()+" needs rebuilding"); -} - -Task *VirtualTarget::build() -{ - state = UPTODATE; - return 0; -} diff --git a/source/virtualtarget.h b/source/virtualtarget.h deleted file mode 100644 index 5151826..0000000 --- a/source/virtualtarget.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef VIRTUALTARGET_H_ -#define VIRTUALTARGET_H_ - -#include "target.h" - -/** -A target that is not associated with any file. -*/ -class VirtualTarget: public Target -{ -public: - VirtualTarget(Builder &b, const std::string &n): Target(b, n) { } - - const char *get_type() const override { return "VirtualTarget"; } -private: - void check_rebuild() override; - -public: - Task *build() override; -}; - -#endif diff --git a/source/vssolutionfile.cpp b/source/vssolutionfile.cpp deleted file mode 100644 index af7e01f..0000000 --- a/source/vssolutionfile.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include -#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(*i)) - { - if(FileTarget *project = builder.get_vfs().get_target(spkg->get_source_directory()/(spkg->get_name()+".vcxproj"))) - add_dependency(*project); - - for(Package *r: spkg->get_required_packages()) - if(!any_equals(reqs, r)) - reqs.push_back(r); - } -} diff --git a/source/vssolutionfile.h b/source/vssolutionfile.h deleted file mode 100644 index 0a97d32..0000000 --- a/source/vssolutionfile.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef VSSOLUTIONFILE_H_ -#define VSSOLUTIONFILE_H_ - -#include "filetarget.h" - -class VsSolutionFile: public FileTarget -{ -public: - VsSolutionFile(Builder &, const SourcePackage &); - - const char *get_type() const override { return "VsSolutionFile"; } -protected: - void find_dependencies() override; -}; - -#endif diff --git a/source/vssolutiongenerator.cpp b/source/vssolutiongenerator.cpp deleted file mode 100644 index 7dce076..0000000 --- a/source/vssolutiongenerator.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include -#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 &, 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 projects; - for(const Target *t: solution.get_dependencies()) - if(const VcxProjectFile *project = dynamic_cast(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 build_types = builder.get_build_types(); - const char *platforms[] = { "x86", "x64" }; - - IO::print(out, "Global\n"); - IO::print(out, "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n"); - for(const string &t: build_types) - for(const char *p: platforms) - IO::print(out, "\t\t%s|%s = %s|%s\n", t, p, t, p); - IO::print(out, "\tEndGlobalSection\n"); - IO::print(out, "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n"); - for(const VcxProjectFile *p: projects) - for(const string &t: build_types) - for(const char *f: platforms) - { - const char *project_platform = (!strcmp(f, "x86") ? "Win32" : f); - IO::print(out, "\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n", p->get_guid(), t, f, t, project_platform); - if(p->get_package()==&spkg) - IO::print(out, "\t\t{%s}.%s|%s.Build.0 = %s|%s\n", p->get_guid(), t, f, t, project_platform); - } - IO::print(out, "\tEndGlobalSection\n"); - IO::print(out, "EndGlobal\n"); - - return true; -} diff --git a/source/vssolutiongenerator.h b/source/vssolutiongenerator.h deleted file mode 100644 index ca33237..0000000 --- a/source/vssolutiongenerator.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef VSSOLUTIONGENERATOR_H_ -#define VSSOLUTIONGENERATOR_H_ - -#include "tool.h" - -class VsSolutionFile; - -class VsSolutionGenerator: public Tool -{ -public: - VsSolutionGenerator(Builder &); - - Target *create_target(const std::vector &, const std::string &) override; - -private: - static bool _run(const VsSolutionFile &); -}; - -#endif