Skip to content

Commit

Permalink
Supports per-target dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon committed Oct 5, 2023
1 parent acd5581 commit ebd7f94
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 82 deletions.
8 changes: 0 additions & 8 deletions stage0/src/dep/mod.rs

This file was deleted.

5 changes: 2 additions & 3 deletions stage0/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::ast::ParseError;
use crate::dep::DepResolver;
use crate::ffi::llvm_init;
use crate::pkg::DependencyResolver;
use crate::project::{Project, ProjectBuildError, ProjectLoadError, ProjectType};
use clap::{command, value_parser, Arg, ArgMatches, Command};
use std::error::Error;
Expand All @@ -11,7 +11,6 @@ use std::process::ExitCode;

mod ast;
mod codegen;
mod dep;
mod ffi;
mod lexer;
mod pkg;
Expand Down Expand Up @@ -52,7 +51,7 @@ fn build(args: &ArgMatches) -> ExitCode {
unsafe { llvm_init() };

// Setup dependency resolver.
let mut resolver = DepResolver::new();
let mut resolver = DependencyResolver::new();

// Open the project.
let path = args.get_one::<PathBuf>("project").unwrap();
Expand Down
32 changes: 32 additions & 0 deletions stage0/src/pkg/dep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use super::{PackageName, PackageVersion};
use std::hash::{Hash, Hasher};

/// An object for resolving package dependencies.
pub struct DependencyResolver {}

impl DependencyResolver {
pub fn new() -> Self {
Self {}
}
}

/// A package dependency.
pub struct Dependency {
name: PackageName,
version: PackageVersion,
}

impl PartialEq for Dependency {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.version.major() == other.version.major()
}
}

impl Eq for Dependency {}

impl Hash for Dependency {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.version.major().hash(state);
}
}
50 changes: 12 additions & 38 deletions stage0/src/pkg/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use super::{ExportedType, PackageName, PackageVersion};
use super::ExportedType;
use std::collections::HashSet;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;

/// Contains information about a Nitro library.
/// A Nitro library.
///
/// A Nitro library is always a shared library. Nitro can consume a static library but cannot
/// produce it. The reason is because it will cause a surprising behavior to the user in the
Expand All @@ -18,47 +17,16 @@ use std::path::Path;
/// There will be two states of `foo` here, which likely to cause a headache to Alice to figure out
/// what wrong with `foo` when Carlos report something is not working.
pub struct Library {
bin: LibraryBinary,
types: HashSet<ExportedType>,
}

impl Library {
const ENTRY_END: u8 = 0;
const ENTRY_TYPES: u8 = 1;

pub fn new() -> Self {
Self {
types: HashSet::new(),
}
}

pub fn add_type(&mut self, ty: ExportedType) {
assert!(self.types.insert(ty));
}

pub fn write_module_definition<F>(
&self,
pkg: &PackageName,
ver: &PackageVersion,
file: F,
) -> Result<(), std::io::Error>
where
F: AsRef<Path>,
{
// Create the file.
let mut file = File::create(file)?;

file.write_all(b"EXPORTS\n")?;

// Dumpt public types.
for ty in &self.types {
for func in ty.funcs() {
file.write_all(b" ")?;
file.write_all(func.mangle(pkg, ver, ty).as_bytes())?;
file.write_all(b"\n")?;
}
}

Ok(())
pub fn new(bin: LibraryBinary, types: HashSet<ExportedType>) -> Self {
Self { bin, types }
}

pub fn serialize<W: Write>(&self, mut w: W) -> Result<(), std::io::Error> {
Expand All @@ -79,3 +47,9 @@ impl Library {
w.write_all(&[Self::ENTRY_END])
}
}

/// A library's binary.
pub enum LibraryBinary {
Bundle(PathBuf),
System(String),
}
2 changes: 1 addition & 1 deletion stage0/src/pkg/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl PackageMeta {
///
/// A package name must start with a lower case ASCII and followed by zero of more 0-9 and a-z (only
/// lower case). The maximum length is 32 characters.
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct PackageName(String);

impl PackageName {
Expand Down
29 changes: 21 additions & 8 deletions stage0/src/pkg/mod.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
pub use self::dep::*;
pub use self::lib::*;
pub use self::meta::*;
pub use self::target::*;
pub use self::ty::*;

use crate::dep::DepResolver;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use thiserror::Error;

mod dep;
mod lib;
mod meta;
mod target;
mod ty;

/// A unpacked Nitro package.
/// An unpacked Nitro package.
///
/// One package can contains only a single executable and a single library, per architecture.
pub struct Package {
meta: PackageMeta,
exes: HashMap<Target, PathBuf>,
libs: HashMap<Target, (PathBuf, Library)>,
exes: HashMap<Target, Binary<PathBuf>>,
libs: HashMap<Target, Binary<Library>>,
}

impl Package {
Expand All @@ -38,8 +39,8 @@ impl Package {

pub fn new(
meta: PackageMeta,
exes: HashMap<Target, PathBuf>,
libs: HashMap<Target, (PathBuf, Library)>,
exes: HashMap<Target, Binary<PathBuf>>,
libs: HashMap<Target, Binary<Library>>,
) -> Self {
assert!(!exes.is_empty() || !libs.is_empty());

Expand Down Expand Up @@ -85,7 +86,7 @@ impl Package {
Ok(())
}

pub fn export<T>(&self, to: T, resolver: &mut DepResolver) -> Result<(), PackageExportError>
pub fn export<T>(&self, to: T, deps: &DependencyResolver) -> Result<(), PackageExportError>
where
T: AsRef<Path>,
{
Expand All @@ -101,6 +102,18 @@ impl Package {
}
}

/// A compiled binary file.
pub struct Binary<T> {
bin: T,
deps: HashSet<Dependency>,
}

impl<T> Binary<T> {
pub fn new(bin: T, deps: HashSet<Dependency>) -> Self {
Self { bin, deps }
}
}

/// Represents an error when a package is failed to open.
#[derive(Debug, Error)]
pub enum PackageOpenError {}
Expand Down
76 changes: 52 additions & 24 deletions stage0/src/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ pub use self::meta::*;

use crate::ast::{ParseError, Public, SourceFile};
use crate::codegen::{BuildError, Codegen, TypeResolver};
use crate::dep::DepResolver;
use crate::lexer::SyntaxError;
use crate::pkg::{
ExportedFunc, ExportedType, FunctionParam, Library, Package, PackageMeta, PrimitiveTarget,
Target, TargetArch, TargetOs, TargetResolveError, TargetResolver, Type,
Binary, DependencyResolver, ExportedFunc, ExportedType, FunctionParam, Library, LibraryBinary,
Package, PackageMeta, PackageName, PackageVersion, PrimitiveTarget, Target, TargetArch,
TargetOs, TargetResolveError, TargetResolver, Type,
};
use std::borrow::Cow;
use std::collections::{HashMap, VecDeque};
use std::collections::{HashMap, HashSet, VecDeque};
use std::error::Error;
use std::ffi::{c_char, CStr, CString};
use std::fmt::{Display, Formatter};
use std::fs::create_dir_all;
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::ptr::null;
use thiserror::Error;
Expand All @@ -25,13 +26,13 @@ pub struct Project<'a> {
path: PathBuf,
meta: ProjectMeta,
sources: HashMap<String, SourceFile>,
resolver: &'a mut DepResolver,
deps: &'a DependencyResolver,
}

impl<'a> Project<'a> {
pub fn open<P: Into<PathBuf>>(
path: P,
resolver: &'a mut DepResolver,
deps: &'a mut DependencyResolver,
) -> Result<Self, ProjectOpenError> {
// Read the project.
let path = path.into();
Expand All @@ -51,7 +52,7 @@ impl<'a> Project<'a> {
path,
meta,
sources: HashMap::new(),
resolver,
deps,
})
}

Expand Down Expand Up @@ -122,13 +123,13 @@ impl<'a> Project<'a> {
let mut libs = HashMap::new();

for target in PrimitiveTarget::ALL.iter().map(|t| Target::Primitive(t)) {
let bins = self.build_for(&target, &mut types, &mut targets)?;
let (exe, lib) = self.build_for(&target, &mut types, &mut targets)?;

if let Some(exe) = bins.exe {
if let Some(exe) = exe {
assert!(exes.insert(target.clone(), exe).is_none());
}

if let Some(lib) = bins.lib {
if let Some(lib) = lib {
assert!(libs.insert(target.clone(), lib).is_none());
}
}
Expand Down Expand Up @@ -183,7 +184,7 @@ impl<'a> Project<'a> {
target: &Target,
types: &mut TypeResolver<'_>,
targets: &mut TargetResolver,
) -> Result<BuildOutputs, ProjectBuildError> {
) -> Result<(Option<Binary<PathBuf>>, Option<Binary<Library>>), ProjectBuildError> {
// Get primitive target.
let pt = match targets.resolve(target) {
Ok(v) => v,
Expand All @@ -194,8 +195,8 @@ impl<'a> Project<'a> {
let pkg = self.meta.package();
let mut cx = Codegen::new(pkg.name(), pkg.version(), pt, types);

// Enumerate the sources.
let mut lib = Library::new();
// Compile source files.
let mut types = HashSet::new();

for (fqtn, src) in &self.sources {
// Check type condition.
Expand Down Expand Up @@ -272,7 +273,7 @@ impl<'a> Project<'a> {

// Export the type.
if let Some(v) = exp {
lib.add_type(v);
assert!(types.insert(v));
}
}

Expand Down Expand Up @@ -325,7 +326,9 @@ impl<'a> Project<'a> {
TargetOs::Win32 => {
let def = dir.join(format!("{}.def", pkg.name()));

if let Err(e) = lib.write_module_definition(pkg.name(), pkg.version(), &def) {
if let Err(e) =
Self::write_module_definition(pkg.name(), pkg.version(), &types, &def)
{
return Err(ProjectBuildError::CreateModuleDefinitionFailed(def, e));
}

Expand All @@ -343,10 +346,13 @@ impl<'a> Project<'a> {
return Err(ProjectBuildError::LinkFailed(out, e));
}

Ok(BuildOutputs {
exe: None,
lib: Some((out, lib)),
})
Ok((
None,
Some(Binary::new(
Library::new(LibraryBinary::Bundle(out), types),
HashSet::new(),
)),
))
}

fn link(linker: &str, args: &[Cow<'static, str>]) -> Result<(), LinkError> {
Expand All @@ -369,11 +375,33 @@ impl<'a> Project<'a> {
Err(LinkError(err.trim_end().to_owned()))
}
}
}

struct BuildOutputs {
exe: Option<PathBuf>,
lib: Option<(PathBuf, Library)>,
fn write_module_definition<'b, F, T>(
pkg: &PackageName,
ver: &PackageVersion,
types: T,
file: F,
) -> Result<(), std::io::Error>
where
F: AsRef<Path>,
T: IntoIterator<Item = &'b ExportedType>,
{
// Create the file.
let mut file = File::create(file)?;

file.write_all(b"EXPORTS\n")?;

// Dumpt public types.
for ty in types {
for func in ty.funcs() {
file.write_all(b" ")?;
file.write_all(func.mangle(pkg, ver, ty).as_bytes())?;
file.write_all(b"\n")?;
}
}

Ok(())
}
}

#[allow(improper_ctypes)]
Expand Down

0 comments on commit ebd7f94

Please sign in to comment.