diff options
author | Irene Knapp <ireneista@gmail.com> | 2020-09-01 20:25:26 -0700 |
---|---|---|
committer | Irene Knapp <ireneista@gmail.com> | 2020-09-01 20:25:26 -0700 |
commit | 0c8e06d588038ae6e24d1898bd33b3ee5a938640 (patch) | |
tree | 74833939e37908366fe2cb71d639dbe641e054a6 |
Initial.
-rw-r--r-- | .envrc | 1 | ||||
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | Cargo.lock | 5 | ||||
-rw-r--r-- | Cargo.nix | 619 | ||||
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | default.nix | 15 | ||||
-rw-r--r-- | shell.nix | 17 | ||||
-rw-r--r-- | src/error.rs | 24 | ||||
-rw-r--r-- | src/main.rs | 25 |
9 files changed, 718 insertions, 0 deletions
diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..be81fed --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +eval "$(lorri direnv)" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..31158d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/target +/result +/result-* +**/*.rs.bk +*.swp diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8d60690 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "shell" +version = "0.1.0" diff --git a/Cargo.nix b/Cargo.nix new file mode 100644 index 0000000..e59159d --- /dev/null +++ b/Cargo.nix @@ -0,0 +1,619 @@ + +# This file was @generated by crate2nix 0.8.0 with the command: +# "generate" +# See https://github.com/kolloch/crate2nix for more info. + +{ nixpkgs ? <nixpkgs> +, pkgs ? import nixpkgs { config = {}; } +, lib ? pkgs.lib +, stdenv ? pkgs.stdenv +, buildRustCrate ? pkgs.buildRustCrate + # This is used as the `crateOverrides` argument for `buildRustCrate`. +, defaultCrateOverrides ? pkgs.defaultCrateOverrides + # The features to enable for the root_crate or the workspace_members. +, rootFeatures ? [ "default" ] + # If true, throw errors instead of issueing deprecation warnings. +, strictDeprecation ? false + # Whether to perform release builds: longer compile times, faster binaries. +, release ? true +}: + +rec { + # + # "public" attributes that we attempt to keep stable with new versions of crate2nix. + # + + rootCrate = rec { + packageId = "shell"; + + # Use this attribute to refer to the derivation building your root crate package. + # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }. + build = internal.buildRustCrateWithFeatures { + inherit packageId; + }; + + # Debug support which might change between releases. + # File a bug if you depend on any for non-debug work! + debug = internal.debugCrate { inherit packageId; }; + }; + root_crate = + internal.deprecationWarning + "root_crate is deprecated since crate2nix 0.4. Please use rootCrate instead." + rootCrate.build; + # Refer your crate build derivation by name here. + # You can override the features with + # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }. + workspaceMembers = { + "shell" = rec { + packageId = "shell"; + build = internal.buildRustCrateWithFeatures { + packageId = "shell"; + }; + + # Debug support which might change between releases. + # File a bug if you depend on any for non-debug work! + debug = internal.debugCrate { inherit packageId; }; + }; + }; + workspace_members = + internal.deprecationWarning + "workspace_members is deprecated in crate2nix 0.4. Please use workspaceMembers instead." + lib.mapAttrs (n: v: v.build) workspaceMembers; + + # + # "internal" ("private") attributes that may change in every new version of crate2nix. + # + + internal = rec { + # Build and dependency information for crates. + # Many of the fields are passed one-to-one to buildRustCrate. + # + # Noteworthy: + # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate. + # but with additional information which is used during dependency/feature resolution. + # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging. + # * `devDependencies` as of now not used by `buildRustCrate` but used to + # inject test dependencies into the build + + crates = { + "shell" = rec { + crateName = "shell"; + version = "0.1.0"; + edition = "2018"; + crateBin = [ + { name = "shell"; path = "src/main.rs"; } + ]; + src = (builtins.filterSource sourceFilter ./.); + authors = [ + "Irene Knapp <ireneista@gmail.com>" + ]; + + }; + }; + + # +# crate2nix/default.nix (excerpt start) +# + + /* Target (platform) data for conditional dependencies. + This corresponds roughly to what buildRustCrate is setting. + */ + defaultTarget = { + unix = true; + windows = false; + fuchsia = true; + test = false; + + # This doesn't appear to be officially documented anywhere yet. + # See https://github.com/rust-lang-nursery/rust-forge/issues/101. + os = if stdenv.hostPlatform.isDarwin + then "macos" + else stdenv.hostPlatform.parsed.kernel.name; + arch = stdenv.hostPlatform.parsed.cpu.name; + family = "unix"; + env = "gnu"; + endian = + if stdenv.hostPlatform.parsed.cpu.significantByte.name == "littleEndian" + then "little" else "big"; + pointer_width = toString stdenv.hostPlatform.parsed.cpu.bits; + vendor = stdenv.hostPlatform.parsed.vendor.name; + debug_assertions = false; + }; + + /* Filters common temp files and build files. */ + # TODO(pkolloch): Substitute with gitignore filter + sourceFilter = name: type: + let + baseName = builtins.baseNameOf (builtins.toString name); + in + ! ( + # Filter out git + baseName == ".gitignore" + || (type == "directory" && baseName == ".git") + + # Filter out build results + || ( + type == "directory" && ( + baseName == "target" + || baseName == "_site" + || baseName == ".sass-cache" + || baseName == ".jekyll-metadata" + || baseName == "build-artifacts" + ) + ) + + # Filter out nix-build result symlinks + || ( + type == "symlink" && lib.hasPrefix "result" baseName + ) + + # Filter out IDE config + || ( + type == "directory" && ( + baseName == ".idea" || baseName == ".vscode" + ) + ) || lib.hasSuffix ".iml" baseName + + # Filter out nix build files + || baseName == "Cargo.nix" + + # Filter out editor backup / swap files. + || lib.hasSuffix "~" baseName + || builtins.match "^\\.sw[a-z]$$" baseName != null + || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null + || lib.hasSuffix ".tmp" baseName + || lib.hasSuffix ".bak" baseName + || baseName == "tests.nix" + ); + + /* Returns a crate which depends on successful test execution + of crate given as the second argument. + + testCrateFlags: list of flags to pass to the test exectuable + testInputs: list of packages that should be available during test execution + */ + crateWithTest = { crate, testCrate, testCrateFlags, testInputs }: + assert builtins.typeOf testCrateFlags == "list"; + assert builtins.typeOf testInputs == "list"; + let + # override the `crate` so that it will build and execute tests instead of + # building the actual lib and bin targets We just have to pass `--test` + # to rustc and it will do the right thing. We execute the tests and copy + # their log and the test executables to $out for later inspection. + test = let + drv = testCrate.override ( + _: { + buildTests = true; + } + ); + in + pkgs.runCommand "run-tests-${testCrate.name}" { + inherit testCrateFlags; + buildInputs = testInputs; + } '' + set -ex + cd ${crate.src} + for file in ${drv}/tests/*; do + $file $testCrateFlags 2>&1 | tee -a $out + done + ''; + in + crate.overrideAttrs ( + old: { + checkPhase = '' + test -e ${test} + ''; + passthru = (old.passthru or {}) // { + inherit test; + }; + } + ); + + /* A restricted overridable version of builtRustCratesWithFeatures. */ + buildRustCrateWithFeatures = + { packageId + , features ? rootFeatures + , crateOverrides ? defaultCrateOverrides + , buildRustCrateFunc ? ( + if crateOverrides == pkgs.defaultCrateOverrides + then buildRustCrate + else buildRustCrate.override { + defaultCrateOverrides = crateOverrides; + } + ) + , runTests ? false + , testCrateFlags ? [] + , testInputs ? [] + }: + lib.makeOverridable + ( + { features, crateOverrides, runTests, testCrateFlags, testInputs }: + let + builtRustCrates = builtRustCratesWithFeatures { + inherit packageId features buildRustCrateFunc; + runTests = false; + }; + builtTestRustCrates = builtRustCratesWithFeatures { + inherit packageId features buildRustCrateFunc; + runTests = true; + }; + drv = builtRustCrates.${packageId}; + testDrv = builtTestRustCrates.${packageId}; + in + if runTests then + crateWithTest { + crate = drv; + testCrate = testDrv; + inherit testCrateFlags testInputs; + } + else drv + ) + { inherit features crateOverrides runTests testCrateFlags testInputs; }; + + /* Returns an attr set with packageId mapped to the result of buildRustCrateFunc + for the corresponding crate. + */ + builtRustCratesWithFeatures = + { packageId + , features + , crateConfigs ? crates + , buildRustCrateFunc + , runTests + , target ? defaultTarget + } @ args: + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isList features); + assert (builtins.isAttrs target); + assert (builtins.isBool runTests); + let + rootPackageId = packageId; + mergedFeatures = mergePackageFeatures ( + args // { + inherit rootPackageId; + target = target // { test = runTests; }; + } + ); + + buildByPackageId = packageId: buildByPackageIdImpl packageId; + + # Memoize built packages so that reappearing packages are only built once. + builtByPackageId = + lib.mapAttrs (packageId: value: buildByPackageId packageId) crateConfigs; + + buildByPackageIdImpl = packageId: + let + features = mergedFeatures."${packageId}" or []; + crateConfig' = crateConfigs."${packageId}"; + crateConfig = + builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ]; + devDependencies = + lib.optionals + (runTests && packageId == rootPackageId) + (crateConfig'.devDependencies or []); + dependencies = + dependencyDerivations { + inherit builtByPackageId features target; + dependencies = + (crateConfig.dependencies or []) + ++ devDependencies; + }; + buildDependencies = + dependencyDerivations { + inherit builtByPackageId features target; + dependencies = crateConfig.buildDependencies or []; + }; + + filterEnabledDependenciesForThis = dependencies: filterEnabledDependencies { + inherit dependencies features target; + }; + + dependenciesWithRenames = + lib.filter (d: d ? "rename") ( + filterEnabledDependenciesForThis + ( + (crateConfig.buildDependencies or []) + ++ (crateConfig.dependencies or []) + ++ devDependencies + ) + ); + + crateRenames = + builtins.listToAttrs + (map (d: { name = d.name; value = d.rename; }) dependenciesWithRenames); + in + buildRustCrateFunc ( + crateConfig // { + src = crateConfig.src or ( + pkgs.fetchurl { + name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; + url = "https://crates.io/api/v1/crates/${crateConfig.crateName}/${crateConfig.version}/download"; + sha256 = crateConfig.sha256; + } + ); + inherit features dependencies buildDependencies crateRenames release; + } + ); + in + builtByPackageId; + + /* Returns the actual derivations for the given dependencies. */ + dependencyDerivations = + { builtByPackageId + , features + , dependencies + , target + }: + assert (builtins.isAttrs builtByPackageId); + assert (builtins.isList features); + assert (builtins.isList dependencies); + assert (builtins.isAttrs target); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies features target; + }; + depDerivation = dependency: builtByPackageId.${dependency.packageId}; + in + map depDerivation enabledDependencies; + + /* Returns a sanitized version of val with all values substituted that cannot + be serialized as JSON. + */ + sanitizeForJson = val: + if builtins.isAttrs val + then lib.mapAttrs (n: v: sanitizeForJson v) val + else if builtins.isList val + then builtins.map sanitizeForJson val + else if builtins.isFunction val + then "function" + else val; + + /* Returns various tools to debug a crate. */ + debugCrate = { packageId, target ? defaultTarget }: + assert (builtins.isString packageId); + let + debug = rec { + # The built tree as passed to buildRustCrate. + buildTree = buildRustCrateWithFeatures { + buildRustCrateFunc = lib.id; + inherit packageId; + }; + sanitizedBuildTree = sanitizeForJson buildTree; + dependencyTree = sanitizeForJson ( + buildRustCrateWithFeatures { + buildRustCrateFunc = crate: { + "01_crateName" = crate.crateName or false; + "02_features" = crate.features or []; + "03_dependencies" = crate.dependencies or []; + }; + inherit packageId; + } + ); + mergedPackageFeatures = mergePackageFeatures { + features = rootFeatures; + inherit packageId target; + }; + diffedDefaultPackageFeatures = diffDefaultPackageFeatures { + inherit packageId target; + }; + }; + in + { internal = debug; }; + + /* Returns differences between cargo default features and crate2nix default + features. + + This is useful for verifying the feature resolution in crate2nix. + */ + diffDefaultPackageFeatures = + { crateConfigs ? crates + , packageId + , target + }: + assert (builtins.isAttrs crateConfigs); + let + prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); + mergedFeatures = + prefixValues + "crate2nix" + (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; }); + configs = prefixValues "cargo" crateConfigs; + combined = lib.foldAttrs (a: b: a // b) {} [ mergedFeatures configs ]; + onlyInCargo = + builtins.attrNames + (lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined); + onlyInCrate2Nix = + builtins.attrNames + (lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined); + differentFeatures = lib.filterAttrs + ( + n: v: + (v ? "crate2nix") + && (v ? "cargo") + && (v.crate2nix.features or []) != (v."cargo".resolved_default_features or []) + ) + combined; + in + builtins.toJSON { + inherit onlyInCargo onlyInCrate2Nix differentFeatures; + }; + + /* Returns an attrset mapping packageId to the list of enabled features. + + If multiple paths to a dependency enable different features, the + corresponding feature sets are merged. Features in rust are additive. + */ + mergePackageFeatures = + { crateConfigs ? crates + , packageId + , rootPackageId ? packageId + , features ? rootFeatures + , dependencyPath ? [ crates.${packageId}.crateName ] + , featuresByPackageId ? {} + , target + # Adds devDependencies to the crate with rootPackageId. + , runTests ? false + , ... + } @ args: + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isString rootPackageId); + assert (builtins.isList features); + assert (builtins.isList dependencyPath); + assert (builtins.isAttrs featuresByPackageId); + assert (builtins.isAttrs target); + assert (builtins.isBool runTests); + let + crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); + expandedFeatures = expandFeatures (crateConfig.features or {}) features; + + depWithResolvedFeatures = dependency: + let + packageId = dependency.packageId; + features = dependencyFeatures expandedFeatures dependency; + in + { inherit packageId features; }; + + resolveDependencies = cache: path: dependencies: + assert (builtins.isAttrs cache); + assert (builtins.isList dependencies); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies target; + features = expandedFeatures; + }; + directDependencies = map depWithResolvedFeatures enabledDependencies; + foldOverCache = op: lib.foldl op cache directDependencies; + in + foldOverCache + ( + cache: { packageId, features }: + let + cacheFeatures = cache.${packageId} or []; + combinedFeatures = sortedUnique (cacheFeatures ++ features); + in + if cache ? ${packageId} && cache.${packageId} == combinedFeatures + then cache + else mergePackageFeatures { + features = combinedFeatures; + featuresByPackageId = cache; + inherit crateConfigs packageId target runTests rootPackageId; + } + ); + + cacheWithSelf = + let + cacheFeatures = featuresByPackageId.${packageId} or []; + combinedFeatures = sortedUnique (cacheFeatures ++ expandedFeatures); + in + featuresByPackageId // { + "${packageId}" = combinedFeatures; + }; + + cacheWithDependencies = + resolveDependencies cacheWithSelf "dep" ( + crateConfig.dependencies or [] + ++ lib.optionals + (runTests && packageId == rootPackageId) + (crateConfig.devDependencies or []) + ); + + cacheWithAll = + resolveDependencies + cacheWithDependencies "build" + (crateConfig.buildDependencies or []); + in + cacheWithAll; + + /* Returns the enabled dependencies given the enabled features. */ + filterEnabledDependencies = { dependencies, features, target }: + assert (builtins.isList dependencies); + assert (builtins.isList features); + assert (builtins.isAttrs target); + + lib.filter + ( + dep: + let + targetFunc = dep.target or (features: true); + in + targetFunc { inherit features target; } + && ( + !(dep.optional or false) + || builtins.any (doesFeatureEnableDependency dep) features + ) + ) + dependencies; + + /* Returns whether the given feature should enable the given dependency. */ + doesFeatureEnableDependency = { name, rename ? null, ... }: feature: + let + prefix = "${name}/"; + len = builtins.stringLength prefix; + startsWithPrefix = builtins.substring 0 len feature == prefix; + in + (rename == null && feature == name) + || (rename != null && rename == feature) + || startsWithPrefix; + + /* Returns the expanded features for the given inputFeatures by applying the + rules in featureMap. + + featureMap is an attribute set which maps feature names to lists of further + feature names to enable in case this feature is selected. + */ + expandFeatures = featureMap: inputFeatures: + assert (builtins.isAttrs featureMap); + assert (builtins.isList inputFeatures); + let + expandFeature = feature: + assert (builtins.isString feature); + [ feature ] ++ (expandFeatures featureMap (featureMap."${feature}" or [])); + outFeatures = builtins.concatMap expandFeature inputFeatures; + in + sortedUnique outFeatures; + + /* + Returns the actual features for the given dependency. + + features: The features of the crate that refers this dependency. + */ + dependencyFeatures = features: dependency: + assert (builtins.isList features); + assert (builtins.isAttrs dependency); + let + defaultOrNil = if dependency.usesDefaultFeatures or true + then [ "default" ] + else []; + explicitFeatures = dependency.features or []; + additionalDependencyFeatures = + let + dependencyPrefix = (dependency.rename or dependency.name) + "/"; + dependencyFeatures = + builtins.filter (f: lib.hasPrefix dependencyPrefix f) features; + in + builtins.map (lib.removePrefix dependencyPrefix) dependencyFeatures; + in + defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; + + /* Sorts and removes duplicates from a list of strings. */ + sortedUnique = features: + assert (builtins.isList features); + assert (builtins.all builtins.isString features); + let + outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) {} features; + outFeaturesUnique = builtins.attrNames outFeaturesSet; + in + builtins.sort (a: b: a < b) outFeaturesUnique; + + deprecationWarning = message: value: + if strictDeprecation + then builtins.throw "strictDeprecation enabled, aborting: ${message}" + else builtins.trace message value; + + # + # crate2nix/default.nix (excerpt end) + # + + }; +} diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..985f7dc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "shell" +version = "0.1.0" +authors = ["Irene Knapp <ireneista@gmail.com>"] +edition = "2018" + +[dependencies] diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..30b44d2 --- /dev/null +++ b/default.nix @@ -0,0 +1,15 @@ +# Use `nix-build`, not `nix build`, when doing development on this file. It +# does a better job of propagating error messages from the build tools upward. + +let pkgs = import <nixpkgs> { }; + crateOverrides = pkgs.defaultCrateOverrides.override { + openssl = pkgs.libressl; + } // { }; + parameterOverrides = { }; + cargo = pkgs.callPackage ./Cargo.nix { + defaultCrateOverrides = crateOverrides; + }; +in +builtins.mapAttrs + (key: value: value.build.override parameterOverrides) + cargo.workspaceMembers diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..d675097 --- /dev/null +++ b/shell.nix @@ -0,0 +1,17 @@ +let + pkgs = import <nixpkgs> {}; + crate2nix-src = pkgs.fetchFromGitHub { + owner = "kolloch"; + repo = "crate2nix"; + rev = "0.8.0"; + sha256 = "17mmf5sqn0fmpqrf52icq92nf1sy5yacwx9vafk43piaq433ba56"; + }; + crate2nix = pkgs.callPackage (import crate2nix-src) { }; +in +pkgs.mkShell { + buildInputs = with pkgs; [ + cargo + crate2nix + rustc + ]; +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..7054551 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,24 @@ +#[derive(Debug)] +pub enum Error { + IO(std::io::Error), +} + +impl std::error::Error for Error { } + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::IO(e) => e.fmt(f), + } + } +} + +impl std::cmp::PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Error::IO(_), Error::IO(_)) => + false, + } + } +} + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5ca6d46 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,25 @@ +pub mod error; + +use error::Error; + + +pub type Result<T> = std::result::Result<T, Error>; + + +fn main() -> Result<()> { + std::process::exit(match repl() { + Ok(()) => 0, + Err(ref e) => { + eprintln!("{}", e); + 1 + } + }) +} + + +fn repl() -> Result<()> { + println!("Hello, terminal!"); + + Ok(()) +} + |