Skip to content

Commit

Permalink
Merge pull request #62 from habitat-sh/psajja/windows-builds
Browse files Browse the repository at this point in the history
Support windows builds
  • Loading branch information
sajjaphani authored Nov 21, 2024
2 parents c16a08c + 794be9e commit 26e52c7
Show file tree
Hide file tree
Showing 16 changed files with 1,038 additions and 112 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
architecture: [x64, arm64]
toolchain: [stable]
exclude:
- os: macos-latest
architecture: x64
- os: windows-latest
architecture: arm64

steps:
- uses: actions/checkout@v4
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Install the latest stable version of Rust from https://rustup.rs/.

Check out this repository, then use Cargo to build and install a static `hab-auto-build` binary.

### Linux/macOS

```bash
# Asks the rust compiler to statically link in the C Runtime
export RUSTFLAGS='-C target-feature=+crt-static'
Expand All @@ -17,6 +19,14 @@ export CARGO_BUILD_TARGET=$(rustc -vV | grep host | sed 's|host: ||')
cargo install --path .
```

### Windows

```powershell
$env:RUSTFLAGS = '-C target-feature=+crt-static'
$env:CARGO_BUILD_TARGET = (rustc -vV | Select-String 'host:' | ForEach-Object { $_ -replace 'host:\s*', '' }).Trim()
cargo install --path .
```

## Configuration

To configure `hab-auto-build`, you need to create a JSON file with the following structure, which allows the tool to locate and manage plans:
Expand Down
2 changes: 2 additions & 0 deletions src/check/artifact/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ pub mod elf;
pub mod macho;
pub mod package;
pub mod script;
#[cfg(target_os = "windows")]
pub mod win;
42 changes: 37 additions & 5 deletions src/check/artifact/package.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
use std::{collections::BTreeSet, fmt::Display, path::PathBuf};

#[cfg(not(target_os = "windows"))]
use std::{
collections::{hash_map::Entry, BTreeSet, HashMap},
collections::{hash_map::Entry, HashMap},
ffi::OsString,
fmt::Display,
path::PathBuf,
};

use owo_colors::OwoColorize;
use serde::{Deserialize, Serialize};

use crate::{
check::{
ArtifactCheck, ArtifactCheckViolation, ArtifactRuleOptions, CheckerContext,
LeveledArtifactCheckViolation, PlanContextConfig, ViolationLevel,
ArtifactCheck, CheckerContext, LeveledArtifactCheckViolation, PlanContextConfig,
ViolationLevel,
},
core::{ArtifactCache, ArtifactContext, PackageDepGlob, PackageIdent, PackagePath},
store::Store,
};

#[cfg(not(target_os = "windows"))]
use crate::check::{ArtifactCheckViolation, ArtifactRuleOptions};

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "rule", content = "metadata")]
pub(crate) enum PackageRule {
Expand Down Expand Up @@ -333,6 +337,20 @@ impl Default for DuplicateRuntimeBinaryOptions {
pub(crate) struct PackageBeforeCheck {}

impl ArtifactCheck for PackageBeforeCheck {
#[cfg(target_os = "windows")]
fn artifact_context_check(
&self,
_store: &Store,
_rules: &PlanContextConfig,
_checker_context: &mut CheckerContext,
_artifact_cache: &mut ArtifactCache,
_artifact_context: &ArtifactContext,
) -> Vec<LeveledArtifactCheckViolation> {
// Currently, we do not know what the violations are for Windows; we will revisit this later.
vec![].into_iter().collect()
}

#[cfg(not(target_os = "windows"))]
fn artifact_context_check(
&self,
_store: &Store,
Expand Down Expand Up @@ -662,6 +680,20 @@ impl ArtifactCheck for PackageBeforeCheck {
pub(crate) struct PackageAfterCheck {}

impl ArtifactCheck for PackageAfterCheck {
#[cfg(target_os = "windows")]
fn artifact_context_check(
&self,
_store: &Store,
_rules: &PlanContextConfig,
_checker_context: &mut CheckerContext,
_artifact_cache: &mut ArtifactCache,
_artifact_context: &ArtifactContext,
) -> Vec<LeveledArtifactCheckViolation> {
// Currently, we do not know what the violations are for Windows; we will revisit this later.
vec![].into_iter().collect()
}

#[cfg(not(target_os = "windows"))]
fn artifact_context_check(
&self,
_store: &Store,
Expand Down
41 changes: 38 additions & 3 deletions src/check/artifact/script.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
use std::{collections::HashSet, fmt::Display, path::PathBuf};
use std::{fmt::Display, path::PathBuf};

#[cfg(not(target_os = "windows"))]
use std::collections::HashSet;

use owo_colors::OwoColorize;

#[cfg(not(target_os = "windows"))]
use path_absolutize::Absolutize;

use serde::{Deserialize, Serialize};

#[cfg(not(target_os = "windows"))]
use tracing::{debug, error};

use crate::{
check::{
ArtifactCheck, ArtifactCheckViolation, ArtifactRuleOptions, CheckerContext,
LeveledArtifactCheckViolation, PlanContextConfig, ViolationLevel,
ArtifactCheck, CheckerContext, LeveledArtifactCheckViolation, PlanContextConfig,
ViolationLevel,
},
core::{ArtifactCache, ArtifactContext, GlobSetExpression, PackageIdent, PackagePath},
store::Store,
};

#[cfg(not(target_os = "windows"))]
use crate::check::{ArtifactCheckViolation, ArtifactRuleOptions};

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "rule", content = "metadata")]
pub(crate) enum ScriptRule {
Expand Down Expand Up @@ -339,14 +350,19 @@ impl Default for UnlistedScriptInterpreterOptions {

#[derive(Debug)]
pub(crate) struct ScriptCheck {
#[allow(dead_code)]
env_interpreters: Vec<String>,
#[allow(dead_code)]
platform_interpreter_paths: Vec<PathBuf>,
}

impl Default for ScriptCheck {
fn default() -> Self {
Self {
#[cfg(not(target_os = "windows"))]
env_interpreters: vec![String::from("env")],
#[cfg(target_os = "windows")]
env_interpreters: vec![String::from("cmd"), String::from("powershell")],
#[cfg(target_os = "linux")]
platform_interpreter_paths: vec![PathBuf::from("/bin/sh"), PathBuf::from("/bin/false")],
#[cfg(target_os = "macos")]
Expand All @@ -355,11 +371,30 @@ impl Default for ScriptCheck {
PathBuf::from("/bin/false"),
PathBuf::from("/usr/bin/env"),
],
#[cfg(target_os = "windows")]
platform_interpreter_paths: vec![
PathBuf::from("C:\\Windows\\System32\\cmd.exe"),
PathBuf::from("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"),
],
}
}
}

impl ArtifactCheck for ScriptCheck {
#[cfg(target_os = "windows")]
fn artifact_context_check(
&self,
_store: &Store,
_rules: &PlanContextConfig,
_checker_context: &mut CheckerContext,
_artifact_cache: &mut ArtifactCache,
_artifact_context: &ArtifactContext,
) -> Vec<LeveledArtifactCheckViolation> {
// Currently, we do not know what the violations are for Windows; we will revisit this later.
vec![].into_iter().collect()
}

#[cfg(not(target_os = "windows"))]
fn artifact_context_check(
&self,
_store: &Store,
Expand Down
98 changes: 98 additions & 0 deletions src/check/artifact/win.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use std::{fmt::Display, path::PathBuf};

use owo_colors::OwoColorize;
use serde::{Deserialize, Serialize};
use tracing::debug;

use crate::{
check::{
ArtifactCheck, CheckerContext, LeveledArtifactCheckViolation, PlanContextConfig,
ViolationLevel,
},
core::{ArtifactCache, ArtifactContext, GlobSetExpression, PackagePath},
store::Store,
};

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "rule", content = "metadata")]
pub(crate) enum PeRule {
#[serde(rename = "library-dependency-not-found")]
LibraryDependencyNotFound(LibraryDependencyNotFound),
}

impl Display for PeRule {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PeRule::LibraryDependencyNotFound(rule) => write!(f, "{}", rule),
}
}
}

#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct LibraryDependencyNotFound {
pub source: PathBuf,
pub library: String,
}

impl Display for LibraryDependencyNotFound {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}: The library {} could not be found in any specified directories or system paths",
self.source
.relative_package_path()
.unwrap()
.display()
.white(),
self.library.yellow()
)
}
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub(crate) struct LibraryDependencyNotFoundOptions {
#[serde(default = "LibraryDependencyNotFoundOptions::level")]
pub level: ViolationLevel,
#[serde(default)]
pub ignored_files: GlobSetExpression,
}

impl LibraryDependencyNotFoundOptions {
fn level() -> ViolationLevel {
ViolationLevel::Error
}
}

impl Default for LibraryDependencyNotFoundOptions {
fn default() -> Self {
Self {
level: Self::level(),
ignored_files: GlobSetExpression::default(),
}
}
}

// A PE (Portable Executable) check on Windows
#[derive(Debug, Default)]
pub(crate) struct PeCheck {}

impl ArtifactCheck for PeCheck {
fn artifact_context_check(
&self,
_store: &Store,
_rules: &PlanContextConfig,
_checker_context: &mut CheckerContext,
_artifact_cache: &mut ArtifactCache,
_artifact_context: &ArtifactContext,
) -> Vec<LeveledArtifactCheckViolation> {
debug!("Skipping artifact context check against plan for issues");
let violations = vec![];
// let mut used_deps = HashSet::new();
// let tdep_artifacts = checker_context
// .tdeps
// .as_ref()
// .expect("Check context missing transitive dep artifacts");

violations.into_iter().collect()
}
}
31 changes: 28 additions & 3 deletions src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,30 @@ use std::{
};

use crate::{
core::{
ArtifactCache, ArtifactContext, PackageIdent, PackageTarget, PlanContext, SourceContext,
},
core::{ArtifactCache, ArtifactContext, PackageIdent, PlanContext, SourceContext},
store::Store,
};

#[cfg(not(target_os = "windows"))]
use crate::core::PackageTarget;

#[cfg(not(target_os = "windows"))]
use color_eyre::{
eyre::{eyre, Result},
Help, SectionExt,
};

use owo_colors::OwoColorize;
use serde::{Deserialize, Serialize};

#[cfg(not(target_os = "windows"))]
use toml_edit::{Array, DocumentMut, Formatted, InlineTable, Value};

use tracing::debug;

#[cfg(target_os = "linux")]
use self::artifact::elf::{ElfCheck, ElfRule, ElfRuleOptions};

#[cfg(target_os = "macos")]
use self::artifact::macho::{MachORule, MachORuleOptions};

Expand Down Expand Up @@ -77,6 +84,7 @@ impl PlanContextConfig {
self
}

#[cfg(not(target_os = "windows"))]
pub fn from_str(value: &str, target: PackageTarget) -> Result<PlanContextConfig> {
let document = value.parse::<DocumentMut>()?;
let mut restructured_document = DocumentMut::new();
Expand Down Expand Up @@ -550,12 +558,16 @@ pub(crate) trait ArtifactCheck {

#[derive(Debug, Default)]
pub(crate) struct CheckerContext {
#[allow(dead_code)]
tdeps: Option<HashMap<PackageIdent, ArtifactContext>>,
#[allow(dead_code)]
runtime_artifacts: Option<Vec<ArtifactContext>>,
#[allow(dead_code)]
unused_deps: Option<HashSet<PackageIdent>>,
}

impl CheckerContext {
#[allow(dead_code)]
pub fn mark_used(&mut self, dep: &PackageIdent) {
if let Some(unused_deps) = self.unused_deps.as_mut() {
unused_deps.remove(dep);
Expand Down Expand Up @@ -595,6 +607,19 @@ impl Checker {
],
}
}
#[cfg(target_os = "windows")]
pub fn new() -> Checker {
use self::artifact::win::PeCheck;
Checker {
source_checks: vec![Box::<LicenseCheck>::default()],
artifact_checks: vec![
Box::<PackageBeforeCheck>::default(),
Box::<PeCheck>::default(),
Box::<ScriptCheck>::default(),
Box::<PackageAfterCheck>::default(),
],
}
}
}

impl SourceCheck for Checker {
Expand Down
Loading

0 comments on commit 26e52c7

Please sign in to comment.