Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target
artifacts
*.exe
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,17 @@ Commands that exist upstream but aren't shipped here because they rely on POSIX-
## Contributing

Bug reports and pull requests are welcome. See [`CONTRIBUTING.md`](./CONTRIBUTING.md) for details on the repo layout and how changes flow between this repo and the upstream uutils projects.

### Local command layout

After building locally, you can create a test layout with per-command entry
points without running the installer:

```powershell
.\scripts\create-dev-links.ps1 -Force
```

The script creates `artifacts\dev-layout\bin\<utility>.exe` and
`artifacts\dev-layout\cmd\<utility>.cmd` entries for the built `coreutils.exe`.
It uses hard links first, because they work on NTFS without elevation. If hard
links are not available, it copies the binary by default.
109 changes: 109 additions & 0 deletions scripts/create-dev-links.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<#
.SYNOPSIS
Creates a local command-link layout for testing Coreutils for Windows.

.DESCRIPTION
Creates bin\<utility>.exe and cmd\<utility>.cmd entries that point at a built
coreutils.exe. Hard links are used first because they do not require elevation
on NTFS. If hard links fail, the default fallback is to copy the binary.
#>

param(
[string]$Coreutils = (Join-Path $PSScriptRoot '..\target\debug\coreutils.exe'),
[string]$OutputDir = (Join-Path $PSScriptRoot '..\artifacts\dev-layout'),
[ValidateSet('Copy', 'Symlink', 'None')]
[string]$Fallback = 'Copy',
[switch]$Force
)

Set-StrictMode -Version 2.0
$ErrorActionPreference = 'Stop'

function Resolve-FullPath([string]$Path) {
return [System.IO.Path]::GetFullPath($Path)
}

function Remove-ExistingFile([string]$Path) {
if (Test-Path -LiteralPath $Path) {
if (-not $Force) {
throw "Target already exists: $Path. Pass -Force to recreate it."
}
Remove-Item -LiteralPath $Path -Force
}
}

function New-CommandLink {
param(
[string]$LinkPath,
[string]$TargetPath
)

Remove-ExistingFile $LinkPath

try {
New-Item -ItemType HardLink -Path $LinkPath -Target $TargetPath | Out-Null
return 'HardLink'
}
catch {
if ($Fallback -eq 'None') {
throw
}
}

if ($Fallback -eq 'Symlink') {
New-Item -ItemType SymbolicLink -Path $LinkPath -Target $TargetPath | Out-Null
return 'Symlink'
}

Copy-Item -LiteralPath $TargetPath -Destination $LinkPath
return 'Copy'
}

$coreutilsPath = Resolve-FullPath $Coreutils
if (-not (Test-Path -LiteralPath $coreutilsPath)) {
throw "coreutils.exe not found: $coreutilsPath"
}

$outputPath = Resolve-FullPath $OutputDir
$binDir = Join-Path $outputPath 'bin'
$cmdDir = Join-Path $outputPath 'cmd'
New-Item -ItemType Directory -Force -Path $binDir, $cmdDir | Out-Null

$utilities = & $coreutilsPath --list
if ($LASTEXITCODE -ne 0) {
throw "Failed to run '$coreutilsPath --list'."
}

$created = @{}
$skipped = @()
foreach ($utility in $utilities) {
$name = $utility.Trim()
if ($name.Length -eq 0) {
continue
}

# Keep the development layout aligned with the installer. The binary maps
# "[" to test, but the installer does not currently create [.exe/[.cmd.
if ($name -eq '[') {
$skipped += $name
continue
}

foreach ($entry in @(
@{ Dir = $binDir; Extension = '.exe' },
@{ Dir = $cmdDir; Extension = '.cmd' }
)) {
$linkPath = Join-Path $entry.Dir "$name$($entry.Extension)"
$kind = New-CommandLink -LinkPath $linkPath -TargetPath $coreutilsPath
$created[$kind] = 1 + ($created[$kind] ?? 0)
}
}

[PSCustomObject]@{
Coreutils = $coreutilsPath
OutputDir = $outputPath
BinDir = $binDir
CmdDir = $cmdDir
Created = $created
Skipped = $skipped
}