Skip to content

Commit

Permalink
Initial Commit for Freight
Browse files Browse the repository at this point in the history
This commit sets up Freight as a way to learn how a package manager and
build tool like cargo works. This initial commit creates a bare bones
setup where we use a justfile to create our executable by hand through
invocations of rustc. This executable is then run to build itself again
with a different configuration to show that we can build the program
with itself, and then that executable is run to show that it was built
by itself successfully. Currently it just outputs that it did, but in
the future we would want to have it do a bit more to show it built fine
and can run.
  • Loading branch information
mgattozzi committed Jul 21, 2023
0 parents commit ed3fcb4
Show file tree
Hide file tree
Showing 7 changed files with 377 additions and 0 deletions.
1 change: 1 addition & 0 deletions .envrc
@@ -0,0 +1 @@
use flake
2 changes: 2 additions & 0 deletions .gitignore
@@ -0,0 +1,2 @@
/target
.direnv
130 changes: 130 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions flake.nix
@@ -0,0 +1,34 @@
{
description = "Learn how Cargo and other build tools/package managers work under the hood by building one";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
rust-overlay.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
};

outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [
(import rust-overlay)
];
pkgs = import nixpkgs {
inherit system overlays;
};
in
with pkgs;
{
devShells.default = mkShell {
buildInputs = [
just
(rust-bin.stable."1.70.0".default.override {
extensions = [ "rust-src" "rust-analyzer" ];
targets = ["x86_64-unknown-linux-gnu"];
})
];
RUST_SRC_PATH = "${rust-bin.stable."1.70.0".default}/lib/rustlib/src/rust/library";
};
}
);
}
11 changes: 11 additions & 0 deletions justfile
@@ -0,0 +1,11 @@
run: build
./target/bootstrap_stage0/freight_stage0
./target/bootstrap_stage1/freight_stage1
build:
mkdir -p target/bootstrap_stage0
# Build crate dependencies
rustc src/lib.rs --edition 2021 --crate-type=lib --crate-name=freight \
--out-dir=target/bootstrap_stage0
# Create the executable
rustc src/main.rs --edition 2021 --crate-type=bin --crate-name=freight_stage0 \
--out-dir=target/bootstrap_stage0 -L target/bootstrap_stage0 --extern freight
150 changes: 150 additions & 0 deletions src/lib.rs
@@ -0,0 +1,150 @@
use std::error::Error;
use std::fmt;
use std::fmt::Display;
use std::path::PathBuf;
use std::process::Command;

pub struct Rustc {
edition: Edition,
crate_type: CrateType,
crate_name: String,
out_dir: PathBuf,
lib_dir: PathBuf,
cfg: Vec<String>,
externs: Vec<String>,
}

impl Rustc {
pub fn builder() -> RustcBuilder {
RustcBuilder {
..Default::default()
}
}

pub fn run(self, path: &str) -> Result<(), Box<dyn Error>> {
Command::new("rustc")
.arg(path)
.arg("--edition")
.arg(self.edition.to_string())
.arg("--crate-type")
.arg(self.crate_type.to_string())
.arg("--crate-name")
.arg(self.crate_name)
.arg("--out-dir")
.arg(self.out_dir)
.arg("-L")
.arg(self.lib_dir)
.args(
self.externs
.into_iter()
.map(|r#extern| ["--extern".into(), r#extern])
.flatten(),
)
.args(
self.cfg
.into_iter()
.map(|cfg| ["--cfg".into(), cfg])
.flatten(),
)
.spawn()?
.wait()?;

Ok(())
}
}

#[derive(Default)]
pub struct RustcBuilder {
edition: Option<Edition>,
crate_type: Option<CrateType>,
crate_name: Option<String>,
out_dir: Option<PathBuf>,
lib_dir: Option<PathBuf>,
cfg: Vec<String>,
externs: Vec<String>,
}

impl RustcBuilder {
pub fn edition(mut self, edition: Edition) -> Self {
self.edition = Some(edition);
self
}
pub fn out_dir(mut self, out_dir: PathBuf) -> Self {
self.out_dir = Some(out_dir);
self
}
pub fn lib_dir(mut self, lib_dir: PathBuf) -> Self {
self.lib_dir = Some(lib_dir);
self
}
pub fn crate_name(mut self, crate_name: String) -> Self {
self.crate_name = Some(crate_name);
self
}
pub fn crate_type(mut self, crate_type: CrateType) -> Self {
self.crate_type = Some(crate_type);
self
}
pub fn cfg(mut self, cfg: String) -> Self {
self.cfg.push(cfg);
self
}
pub fn externs(mut self, r#extern: String) -> Self {
self.externs.push(r#extern);
self
}

pub fn done(self) -> Rustc {
Rustc {
edition: self.edition.unwrap_or(Edition::E2015),
crate_type: self.crate_type.expect("Crate type given"),
crate_name: self.crate_name.expect("Crate name given"),
out_dir: self.out_dir.expect("Out dir given"),
lib_dir: self.lib_dir.expect("Lib dir given"),
cfg: self.cfg,
externs: self.externs,
}
}
}

pub enum Edition {
E2015,
E2018,
E2021,
}

impl Display for Edition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let edition = match self {
Self::E2015 => "2015",
Self::E2018 => "2018",
Self::E2021 => "2021",
};
write!(f, "{edition}")
}
}

pub enum CrateType {
Bin,
Lib,
RLib,
DyLib,
CDyLib,
StaticLib,
ProcMacro,
}

impl Display for CrateType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let crate_type = match self {
Self::Bin => "bin",
Self::Lib => "lib",
Self::RLib => "rlib",
Self::DyLib => "dylib",
Self::CDyLib => "cdylib",
Self::StaticLib => "staticlib",
Self::ProcMacro => "proc-macro",
};
write!(f, "{crate_type}")
}
}
49 changes: 49 additions & 0 deletions src/main.rs
@@ -0,0 +1,49 @@
#[cfg(not(stage1))]
use freight::CrateType;
#[cfg(not(stage1))]
use freight::Edition;
#[cfg(not(stage1))]
use freight::Rustc;
#[cfg(not(stage1))]
use std::fs;
#[cfg(not(stage1))]
use std::path::PathBuf;
#[cfg(not(stage1))]
const BOOTSTRAP_STAGE1: &str = "bootstrap_stage1";

use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
#[cfg(not(stage1))]
{
let target_dir = PathBuf::from("target");
let bootstrap_dir = target_dir.join(BOOTSTRAP_STAGE1);
fs::create_dir_all(&bootstrap_dir)?;
Rustc::builder()
.edition(Edition::E2021)
.crate_type(CrateType::Lib)
.crate_name("freight".into())
.out_dir(bootstrap_dir.clone())
.lib_dir(bootstrap_dir.clone())
.cfg("stage1".into())
.done()
.run("src/lib.rs")?;
Rustc::builder()
.edition(Edition::E2021)
.crate_type(CrateType::Bin)
.crate_name("freight_stage1".into())
.out_dir(bootstrap_dir.clone())
.lib_dir(bootstrap_dir)
.cfg("stage1".into())
.externs("freight".into())
.done()
.run("src/main.rs")?;

println!("Completed Stage1 Build");
}
#[cfg(stage1)]
{
println!("Bootstrapped successfully!");
}
Ok(())
}

0 comments on commit ed3fcb4

Please sign in to comment.