Initial LLVM build
This commit is contained in:
parent
cdfbcf4f61
commit
4a35de9a69
10 changed files with 342 additions and 23 deletions
|
|
@ -22,6 +22,7 @@ target_link_libraries(Toolchain
|
|||
target_sources(Toolchain
|
||||
PRIVATE
|
||||
"Main.cpp"
|
||||
"Source/Common.cpp"
|
||||
"Source/Command.cpp"
|
||||
"Source/Download.cpp"
|
||||
"Source/Package.cpp"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef TOOLCHAIN_COMMAND_HPP
|
||||
#define TOOLCHAIN_COMMAND_HPP
|
||||
|
||||
#include "Common.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Toolchain
|
||||
{
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
Command();
|
||||
|
||||
virtual ~Command() = default;
|
||||
|
||||
virtual bool Run() = 0;
|
||||
};
|
||||
|
||||
class CMakeCommand: public Command
|
||||
{
|
||||
public:
|
||||
|
||||
private:
|
||||
fs::path mSource;
|
||||
fs::path mBuild;
|
||||
fs::path mInstall;
|
||||
std::vector<std::pair<std::string, std::string>> mOptions;
|
||||
};
|
||||
|
||||
class NinjaCommand: public Command
|
||||
{
|
||||
public:
|
||||
|
||||
private:
|
||||
fs::path mBuild;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TOOLCHAIN_COMMAND_HPP
|
||||
|
|
@ -2,11 +2,22 @@
|
|||
#define TOOLCHAIN_COMMON_HPP
|
||||
|
||||
#include <filesystem> // IWYU pragma: export
|
||||
#include <string_view>
|
||||
|
||||
namespace Toolchain
|
||||
{
|
||||
// NOLINTNEXTLINE: We're defining this to make life easier.
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
enum class FileType
|
||||
{
|
||||
Unknown, Zip, Tar,
|
||||
};
|
||||
|
||||
// Note(3011): This function should handle both system paths, and URLs.
|
||||
FileType DetectFileType(std::string_view file);
|
||||
|
||||
std::string_view ToString(FileType fileType);
|
||||
}
|
||||
|
||||
#endif // TOOLCHAIN_COMMON_HPP
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
namespace Toolchain
|
||||
{
|
||||
bool DownloadFile(const fs::path &path, std::string_view url);
|
||||
|
||||
// TODO(3011): Archive verification ...
|
||||
}
|
||||
|
||||
#endif // TOOLCHAIN_DOWNLOAD_HPP
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
#ifndef TOOLCHAIN_PACKAGE_HPP
|
||||
#define TOOLCHAIN_PACKAGE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Toolchain
|
||||
{
|
||||
// The package should have a name, version, source url, and it should describe
|
||||
|
|
@ -25,9 +28,16 @@ namespace Toolchain
|
|||
// with all the other projects. It will still require system dependencies for certain bits, but
|
||||
// that doesn't matter too much, as long as the host has a reasonably complete package registry.
|
||||
|
||||
using Step = std::vector<std::string>;
|
||||
|
||||
class Package
|
||||
{
|
||||
public:
|
||||
Package();
|
||||
|
||||
private:
|
||||
std::string mName;
|
||||
std::string mUrl;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
23
Toolchain/Include/Stage.hpp
Normal file
23
Toolchain/Include/Stage.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef TOOLCHAIN_STAGE_HPP
|
||||
#define TOOLCHAIN_STAGE_HPP
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "Package.hpp"
|
||||
|
||||
namespace Toolchain
|
||||
{
|
||||
class Stage
|
||||
{
|
||||
public:
|
||||
Stage(const fs::path &path);
|
||||
|
||||
void AddPackage(const Package &package);
|
||||
|
||||
void Build();
|
||||
|
||||
private:
|
||||
fs::path mDirectory;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TOOLCHAIN_STAGE_HPP
|
||||
|
|
@ -1,12 +1,83 @@
|
|||
#include "Common.hpp"
|
||||
#include "Download.hpp"
|
||||
#include "Unpack.hpp"
|
||||
|
||||
#include <Process/Environment.hpp>
|
||||
#include <Process/Process.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <print>
|
||||
|
||||
int main()
|
||||
{
|
||||
std::println("Hello World!");
|
||||
|
||||
if (!Toolchain::DownloadFile("index.html", "https://3011.io/")) {
|
||||
Toolchain::fs::create_directories("Archives");
|
||||
|
||||
if (!Toolchain::fs::exists("Archives/LLVM.tar.gz") && !Toolchain::DownloadFile("Archives/LLVM.tar.gz", "https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-21.1.8.tar.gz")) {
|
||||
std::println("Unlucky");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Toolchain::fs::create_directories("Sources");
|
||||
|
||||
if (!Toolchain::fs::exists("Sources/LLVM") && !Toolchain::UnpackArchive("Archives/LLVM.tar.gz", "Sources/LLVM")) {
|
||||
std::println("Unlucky");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
Toolchain::fs::create_directories("Builds");
|
||||
|
||||
Process::Process configure(
|
||||
"cmake", { "-S", "Sources/LLVM/llvm", "-B", "Builds/LLVM", "-G", "Ninja", "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_INSTALL_PREFIX=Stage0", "-DLLVM_ENABLE_PROJECTS=all", "-DLLVM_ENABLE_RUNTIMES=all" },
|
||||
Process::ProcessOptions {
|
||||
.StdinMode = Process::PipeMode::Null,
|
||||
.StdoutMode = Process::PipeMode::Inherit,
|
||||
.StderrMode = Process::PipeMode::Inherit,
|
||||
.PathResolution = Process::PathMode::CurrentEnvironment,
|
||||
.Environment = Process::CurrentEnvironment(),
|
||||
.CloseOtherFds = true,
|
||||
}
|
||||
);
|
||||
configure.Wait();
|
||||
if (configure.ExitCode() != 0) {
|
||||
std::println("Unlucky");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Process::Process build (
|
||||
"ninja", { "-C", "Builds/LLVM", "-j4" },
|
||||
Process::ProcessOptions {
|
||||
.StdinMode = Process::PipeMode::Null,
|
||||
.StdoutMode = Process::PipeMode::Inherit,
|
||||
.StderrMode = Process::PipeMode::Inherit,
|
||||
.PathResolution = Process::PathMode::CurrentEnvironment,
|
||||
.Environment = Process::CurrentEnvironment(),
|
||||
.CloseOtherFds = true,
|
||||
}
|
||||
);
|
||||
build.Wait();
|
||||
if (build.ExitCode() != 0) {
|
||||
std::println("Unlucky");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Process::Process install (
|
||||
"ninja", { "-C", "Builds/LLVM", "install" },
|
||||
Process::ProcessOptions {
|
||||
.StdinMode = Process::PipeMode::Null,
|
||||
.StdoutMode = Process::PipeMode::Inherit,
|
||||
.StderrMode = Process::PipeMode::Inherit,
|
||||
.PathResolution = Process::PathMode::CurrentEnvironment,
|
||||
.Environment = Process::CurrentEnvironment(),
|
||||
.CloseOtherFds = true,
|
||||
}
|
||||
);
|
||||
install.Wait();
|
||||
if (install.ExitCode() != 0){
|
||||
std::println("Unlucky");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
43
Toolchain/Source/Common.cpp
Normal file
43
Toolchain/Source/Common.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#include "Common.hpp"
|
||||
|
||||
#include <print>
|
||||
|
||||
namespace Toolchain
|
||||
{
|
||||
FileType DetectFileType(std::string_view file)
|
||||
{
|
||||
if (std::size_t pos = file.find_last_of("?"); pos != std::string_view::npos) {
|
||||
file = file.substr(0, pos);
|
||||
}
|
||||
|
||||
if (std::size_t pos = file.find_last_of("#"); pos != std::string_view::npos) {
|
||||
file = file.substr(0, pos);
|
||||
}
|
||||
|
||||
if (std::size_t pos = file.find_last_of("/"); pos != std::string_view::npos) {
|
||||
file = file.substr(pos + 1);
|
||||
}
|
||||
|
||||
if (file.ends_with(".zip")) {
|
||||
return FileType::Zip;
|
||||
}
|
||||
|
||||
if (file.contains(".tar")) {
|
||||
return FileType::Tar;
|
||||
}
|
||||
|
||||
return FileType::Unknown;
|
||||
}
|
||||
|
||||
std::string_view ToString(FileType fileType)
|
||||
{
|
||||
switch (fileType) {
|
||||
case FileType::Unknown:
|
||||
return "Unknown";
|
||||
case FileType::Zip:
|
||||
return "zip";
|
||||
case FileType::Tar:
|
||||
return "tar";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ namespace Toolchain
|
|||
{
|
||||
bool DownloadFile(const fs::path &path, std::string_view url)
|
||||
{
|
||||
std::println("Downloading {} to {}", url, path.c_str());
|
||||
using offset_t = cpr::cpr_off_t;
|
||||
|
||||
std::ofstream file(path, std::ios::binary);
|
||||
|
|
@ -43,10 +44,38 @@ namespace Toolchain
|
|||
}
|
||||
),
|
||||
cpr::ProgressCallback(
|
||||
[path] (offset_t dt, offset_t dp, offset_t ut, offset_t up, std::intptr_t) {
|
||||
[path] (offset_t dt, offset_t dp, offset_t, offset_t, std::intptr_t) {
|
||||
if (!dt) {
|
||||
std::string unit = "B";
|
||||
double current = dp;
|
||||
if (current >= 1024.0) {
|
||||
current /= 1024.0;
|
||||
unit = "KiB";
|
||||
}
|
||||
if (current >= 1024.0) {
|
||||
current /= 1024.0;
|
||||
unit = "MiB";
|
||||
}
|
||||
std::print("\rDownloading {}: {}{}", path.c_str(), current, unit);
|
||||
}
|
||||
else {
|
||||
std::string unit = "B";
|
||||
double current = dp;
|
||||
double total = dt;
|
||||
if (total >= 1024.0) {
|
||||
total /= 1024.0;
|
||||
current /= 1024.0;
|
||||
unit = "KiB";
|
||||
}
|
||||
if (total >= 1024.0) {
|
||||
total /= 1024.0;
|
||||
current /= 1024.0;
|
||||
unit = "MiB";
|
||||
}
|
||||
double progress = double(dp) / double(dt);
|
||||
std::print("\rDownloading {}: {:.2f}% ({}/{})",
|
||||
path.c_str(), progress * 100, dp, dt);
|
||||
std::print("\rDownloading {}: {:.2f}% ({}/{}{})",
|
||||
path.c_str(), progress * 100, current, total, unit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
)
|
||||
|
|
@ -62,4 +91,18 @@ namespace Toolchain
|
|||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string DownloadContent(std::string_view url)
|
||||
{
|
||||
std::println("Downloading {}", url);
|
||||
cpr::Response response = cpr::Get(cpr::Url(url));
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::println("Download failed with status: {}, {}",
|
||||
response.status_code, response.status_line);
|
||||
return {};
|
||||
}
|
||||
|
||||
return response.text;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,107 @@
|
|||
#include "Unpack.hpp"
|
||||
#include "Common.hpp"
|
||||
|
||||
#include <Process/Process.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <print>
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
bool UnpackZip(const fs::path &sourceArchive, const fs::path &destinationDirectory)
|
||||
{
|
||||
Process::Process unzip(
|
||||
"unzip", { "-o", sourceArchive.string(), "-d", destinationDirectory.string() },
|
||||
Process::ProcessOptions {
|
||||
.StdinMode = Process::PipeMode::Null,
|
||||
.StdoutMode = Process::PipeMode::Null,
|
||||
.StderrMode = Process::PipeMode::Null,
|
||||
.PathResolution = Process::PathMode::CurrentEnvironment,
|
||||
.Environment = {},
|
||||
.CloseOtherFds = true,
|
||||
}
|
||||
);
|
||||
unzip.Wait();
|
||||
return unzip.ExitCode() == 0;
|
||||
}
|
||||
|
||||
bool UnpackTar(const fs::path &sourceArchive, const fs::path &destinationDirectory)
|
||||
{
|
||||
Process::Process untar(
|
||||
"tar", { "-xf", sourceArchive.string(), "-C", destinationDirectory.string() },
|
||||
Process::ProcessOptions {
|
||||
.StdinMode = Process::PipeMode::Null,
|
||||
.StdoutMode = Process::PipeMode::Null,
|
||||
.StderrMode = Process::PipeMode::Null,
|
||||
.PathResolution = Process::PathMode::CurrentEnvironment,
|
||||
.Environment = {},
|
||||
.CloseOtherFds = true,
|
||||
}
|
||||
);
|
||||
untar.Wait();
|
||||
return untar.ExitCode() == 0;
|
||||
}
|
||||
|
||||
std::vector<fs::path> GetEntries(const fs::path &directory)
|
||||
{
|
||||
std::vector<fs::path> result;
|
||||
for (const auto &entry: fs::directory_iterator(directory)) {
|
||||
result.push_back(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Toolchain
|
||||
{
|
||||
bool UnpackArchive(const fs::path &sourceArchive, const fs::path &destinationDirectory)
|
||||
{
|
||||
// Select the command based on the file extension
|
||||
if (fs::exists(destinationDirectory)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract the archive into a temporary directory
|
||||
FileType fileType = DetectFileType(sourceArchive.string());
|
||||
if (fileType == FileType::Unknown ||
|
||||
(fileType != FileType::Zip && fileType != FileType::Tar)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check how many items were extracted (non-recursively, we only care about the top level)
|
||||
|
||||
// If there was only one item, and it's a directory, it should be moved (and renamed)
|
||||
// to the destination. In case it's a file, the destination should be created, and the
|
||||
// file moved there.
|
||||
|
||||
// If there are multiple items, the destination will be created, and all items moved into
|
||||
// the destination.
|
||||
|
||||
// Error handling and cleanup should be done so the temporary directory is always
|
||||
// kept clean in case the program tries to use it for other stuff.
|
||||
|
||||
// The reasoning behind this sort of behaviour is that the whole thing should always be as
|
||||
// consistent as possible, we want everything to be as consistent as possible, and easy to
|
||||
// work with afterwards. The point is that for the most part, the structure is know, so
|
||||
// it is easy to proceed, and otherwise it should be easy to analyze the contents
|
||||
// programatically.
|
||||
fs::path stagingDirectory = destinationDirectory.parent_path() / "UnpackStaging";
|
||||
if (fs::exists(stagingDirectory)) {
|
||||
return false;
|
||||
}
|
||||
fs::create_directories(stagingDirectory);
|
||||
|
||||
if (fileType == FileType::Zip) {
|
||||
if (!UnpackZip(sourceArchive, stagingDirectory)) {
|
||||
fs::remove_all(stagingDirectory);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (fileType == FileType::Tar) {
|
||||
if (!UnpackTar(sourceArchive, stagingDirectory)) {
|
||||
fs::remove_all(stagingDirectory);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Note(3011): Rename will fail between different filesystems.
|
||||
// This situation will likely never happen for intended workloads,
|
||||
// but in case it does happen, a fallback to copy + remove will be necessary.
|
||||
|
||||
std::vector<fs::path> entries = GetEntries(stagingDirectory);
|
||||
if (entries.size() == 1 && fs::is_directory(entries[0])) {
|
||||
fs::rename(entries[0], destinationDirectory);
|
||||
fs::remove_all(stagingDirectory);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note(3011): The archive was either empty, or there are multiple entries,
|
||||
// we don't really care at this point.
|
||||
fs::rename(stagingDirectory, destinationDirectory);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue