Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
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
15 changes: 5 additions & 10 deletions 15 .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
"isDefault": true
},
"type": "shell",
"command": "${workspaceFolder}\\build.ps1",
"command": "${workspaceFolder}/build.ps1",
"options": {
"cwd": "${workspaceFolder}"
},
"presentation": {
"echo": true,
"reveal": "silent",
Expand All @@ -27,18 +30,10 @@
"isDefault": true
},
"type": "shell",
"command": "${workspaceFolder}/test.ps1",
"options": {
"cwd": "${workspaceFolder}",
"env": {
"PSModulePath": "${workspaceFolder}\\Output;${env:PSModulePath}"
}
},
"command": "Invoke-Pester",
"args": [
"${workspaceFolder}\\Tests",
"-PesterOption", "@{ IncludeVSCodeMarker = $True }",
"-CodeCoverage", "${workspaceFolder}\\Output\\*.psm1"
],
"presentation": {
"echo": true,
"reveal": "always",
Expand Down
7 changes: 5 additions & 2 deletions 7 ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,15 @@ For best results, you need to organize your module project similarly to how this

1. Create a `source` folder with a `build.psd1` file and your module manifest in it
2. In the `build.psd1` specify the relative **Path** to your module's manifest, e.g. `@{ Path = "ModuleBuilder.psd1" }`
3. In your manifest, make sure the `FunctionsToExport` entry is not commented out. You can leave it empty
3. In your manifest, make sure a few values are not commented out. You can leave them empty, because they'll be overwritten:
- `FunctionsToExport` will be updated with the _file names_ that match the `PublicFilter`
- `AliasesToExport` will be updated with the values from `[Alias()]` attributes on commands
- `Prerelease` and `ReleaseNotes` in the `PSData` hashtable in `PrivateData`

Once you start working on the module, you'll create sub-folders in source, and put script files in them with only **one** function in each file. You should name the files with _the same name_ as the function that's in them -- especially in the public folder, where we use the file name (without the extension) to determine the exported functions.

1. By convention, use folders named "Classes" (and/or "Enum"), "Private", and "Public"
2. By convention, the functions in "Public" will be exported from the module
2. By convention, the functions in "Public" will be exported from the module (you can override the `PublicFilter`)
3. To force classes to be in a certain order, you can prefix their file names with numbers, like `01-User.ps1`

There are a *lot* of conventions in `Build-Module`, expressed as default values for its parameters. These defaults are documented in the help for Build-Module. You can override any parameter to `Build-Module` by passing it, or by adding keys to the `build.psd1` file with your preferences.
118 changes: 81 additions & 37 deletions 118 Source/Private/GetBuildInfo.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,7 @@ function GetBuildInfo {
[CmdletBinding()]
param(
# The path to the Build Manifest Build.psd1
[Parameter(Mandatory)]
[ValidateScript( {
if ((Test-Path $_) -and (Split-path -Leaf $_) -eq 'build.psd1') {
$true
}
else {
throw "The Module Manifest must point to a valid build.psd1 Data file"
}
})]
[Parameter()][AllowNull()]
[string]$BuildManifest,

# Pass MyInvocation from the Build-Command so we can read parameter values
Expand All @@ -19,60 +11,112 @@ function GetBuildInfo {
$BuildCommandInvocation
)

# Read the Module Manifest configuration file for default parameter values
Write-Debug "Load Build Manifest $BuildManifest"
$BuildInfo = Import-Metadata -Path $BuildManifest
$BuildInfo = if ($BuildManifest -and (Test-Path $BuildManifest)) {
if ((Split-path -Leaf $BuildManifest) -eq 'build.psd1') {
# Read the Module Manifest configuration file for default parameter values
Write-Debug "Load Build Manifest $BuildManifest"
Import-Metadata -Path $BuildManifest
} else {
Write-Debug "Use SourcePath $BuildManifest"
@{ SourcePath = $BuildManifest }
}
} else {
@{}
}

$CommonParameters = [System.Management.Automation.Cmdlet]::CommonParameters +
[System.Management.Automation.Cmdlet]::OptionalCommonParameters
$BuildParameters = $BuildCommandInvocation.MyCommand.Parameters
# Make we can always look things up in BoundParameters
$BoundParameters = if ($BuildCommandInvocation.BoundParameters) {
$BuildCommandInvocation.BoundParameters
} else {
@{}
}

# Combine the defaults with parameter values
$ParameterValues = @{}
if ($BuildCommandInvocation) {
foreach ($parameter in $BuildParameters.GetEnumerator().Where({$_.Key -notin $CommonParameters})) {
Write-Debug " Parameter: $($parameter.key)"
$key = $parameter.Key
# set if it doesn't exist, overwrite if the value is bound as a parameter
if (!$BuildInfo.ContainsKey($key) -or ($BuildCommandInvocation.BoundParameters -and $BuildCommandInvocation.BoundParameters.ContainsKey($key))) {

# We want to map the parameter aliases to the parameter name:
foreach ($k in @($parameter.Value.Aliases)) {
if ($null -ne $k -and $BuildInfo.ContainsKey($k)) {
Write-Debug " ... Update BuildInfo[$key] from $k"
$BuildInfo[$key] = $BuildInfo[$k]
$null = $BuildInfo.Remove($k)
}
}
# Bound parameter values > build.psd1 values > default parameters values
if (-not $BuildInfo.ContainsKey($key) -or $BoundParameters.ContainsKey($key)) {
# Reading the current value of the $key variable returns either the bound parameter or the default
if ($null -ne ($value = Get-Variable -Name $key -ValueOnly -ErrorAction Ignore )) {
if ($value -ne ($null -as $parameter.Value.ParameterType)) {
$ParameterValues[$key] = $value
}
}
} else {
Write-Debug " From Manifest: $($BuildInfo.$key)"
if ($BoundParameters.ContainsKey($key)) {
Write-Debug " From Parameter: $($ParameterValues[$key] -join ', ')"
} elseif ($ParameterValues[$key]) {
Write-Debug " From Default: $($ParameterValues[$key] -join ', ')"
}
} elseif ($BuildInfo[$key]) {
Write-Debug " From Manifest: $($BuildInfo[$key] -join ', ')"
}
Write-Debug " From Parameter: $($ParameterValues.$key)"
}
}
# BuildInfo.SourcePath should point to a module manifest
if ($BuildInfo.SourcePath -and $BuildInfo.SourcePath -ne $BuildManifest) {
$ParameterValues["SourcePath"] = $BuildInfo.SourcePath
}
# If SourcePath point to build.psd1, we should clear it
if ($ParameterValues["SourcePath"] -eq $BuildManifest) {
$ParameterValues.Remove("SourcePath")
}
Write-Debug "Finished parsing Build Manifest $BuildManifest"

$BuildInfo = $BuildInfo | Update-Object $ParameterValues
Write-Debug "Using Module Manifest $($BuildInfo.SourcePath)"

# Resolve Build Manifest's parent folder to find the Absolute path
$BuildManifestParent = (Split-Path -Parent $BuildManifest)

# Resolve Module manifest if not defined in Build.psd1
if (-Not $BuildInfo.ModuleManifest) {
$ModuleName = Split-Path -Leaf $BuildManifestParent
$BuildManifestParent = if ($BuildManifest) {
Split-Path -Parent $BuildManifest
} else {
Get-Location -PSProvider FileSystem
}

# If we're in a "well known" source folder, look higher for a name
if ($ModuleName -in 'Source', 'src') {
$ModuleName = Split-Path (Split-Path -Parent $BuildManifestParent) -Leaf
if (-Not $BuildInfo.SourcePath) {
# Find a module manifest (or maybe several)
$ModuleInfo = Get-ChildItem $BuildManifestParent -Recurse -Filter *.psd1 -ErrorAction SilentlyContinue |
ImportModuleManifest -ErrorAction SilentlyContinue
# If we found more than one module info, the only way we have of picking just one is if it matches a folder name
if (@($ModuleInfo).Count -gt 1) {
# Resolve Build Manifest's parent folder to find the Absolute path
$ModuleName = Split-Path -Leaf $BuildManifestParent
# If we're in a "well known" source folder, look higher for a name
if ($ModuleName -in 'Source', 'src') {
$ModuleName = Split-Path (Split-Path -Parent $BuildManifestParent) -Leaf
}
$ModuleInfo = @($ModuleInfo).Where{ $_.Name -eq $ModuleName }
}
if (@($ModuleInfo).Count -eq 1) {
Write-Debug "Updating BuildInfo SourcePath to $($ModuleInfo.Path)"
$BuildInfo = $BuildInfo | Update-Object @{ SourcePath = $ModuleInfo.Path }
}
if (-Not $BuildInfo.SourcePath) {
throw "Can't find a module manifest in $BuildManifestParent"
}

# As the Module Manifest did not specify the Module manifest, we expect the Module manifest in same folder
$ModuleManifest = Join-Path $BuildManifestParent "$ModuleName.psd1"
Write-Debug "Updating BuildInfo path to $ModuleManifest"
$BuildInfo = $BuildInfo | Update-Object @{ModuleManifest = $ModuleManifest }
}

# Make sure the Path is set and points at the actual manifest, relative to Build.psd1 or absolute
if(!(Split-Path -IsAbsolute $BuildInfo.ModuleManifest)) {
$BuildInfo.ModuleManifest = Join-Path $BuildManifestParent $BuildInfo.ModuleManifest
# Make sure the SourcePath is absolute and points at an actual file
if (!(Split-Path -IsAbsolute $BuildInfo.SourcePath) -and $BuildManifestParent) {
$BuildInfo.SourcePath = Join-Path $BuildManifestParent $BuildInfo.SourcePath | Convert-Path
} else {
$BuildInfo.SourcePath = Convert-Path $BuildInfo.SourcePath
}

if (!(Test-Path $BuildInfo.ModuleManifest)) {
throw "Can't find module manifest at $($BuildInfo.ModuleManifest)"
if (!(Test-Path $BuildInfo.SourcePath)) {
throw "Can't find module manifest at the specified SourcePath: $($BuildInfo.SourcePath)"
}

$BuildInfo
Expand Down
36 changes: 36 additions & 0 deletions 36 Source/Private/ImportModuleManifest.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
function ImportModuleManifest {
[CmdletBinding()]
param(
[Alias("PSPath")]
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[string]$Path
)
process {
# Get all the information in the module manifest
$ModuleInfo = Get-Module $Path -ListAvailable -WarningAction SilentlyContinue -ErrorAction SilentlyContinue -ErrorVariable Problems

# Some versions fails silently. If the GUID is empty, we didn't get anything at all
if ($ModuleInfo.Guid -eq [Guid]::Empty) {
Write-Error "Cannot parse '$Path' as a module manifest, try Test-ModuleManifest for details"
return
}

# Some versions show errors are when the psm1 doesn't exist (yet), but we don't care
$ErrorsWeIgnore = "^" + (@(
"Modules_InvalidRequiredModulesinModuleManifest"
"Modules_InvalidRootModuleInModuleManifest"
) -join "|^")

# If there are any OTHER problems we'll fail
if ($Problems = $Problems.Where({ $_.FullyQualifiedErrorId -notmatch $ErrorsWeIgnore })) {
foreach ($problem in $Problems) {
Write-Error $problem
}
# Short circuit - don't output the ModuleInfo if there were errors
return
}

# Workaround the fact that Get-Module returns the DefaultCommandPrefix as Prefix
Update-Object -InputObject $ModuleInfo -UpdateObject @{ DefaultCommandPrefix = $ModuleInfo.Prefix; Prefix = "" }
}
}
31 changes: 10 additions & 21 deletions 31 Source/Private/InitializeBuild.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,18 @@ function InitializeBuild {
# GetBuildInfo reads the parameter values from the Build-Module command and combines them with the Manifest values
$BuildManifest = ResolveBuildManifest $SourcePath

Write-Debug "BuildCommand: $($BuildCommandInvocation.MyCommand | Out-String)"
Write-Debug "BuildCommand: $(
@(
@($BuildCommandInvocation.MyCommand.Name)
@($BuildCommandInvocation.BoundParameters.GetEnumerator().ForEach{ "-{0} '{1}'" -f $_.Key, $_.Value })
) -join ' ')"
$BuildInfo = GetBuildInfo -BuildManifest $BuildManifest -BuildCommandInvocation $BuildCommandInvocation

# These errors are caused by trying to parse valid module manifests without compiling the module first
$ErrorsWeIgnore = "^" + @(
"Modules_InvalidRequiredModulesinModuleManifest"
"Modules_InvalidRootModuleInModuleManifest"
) -join "|^"

# Finally, add all the information in the module manifest to the return object
$ModuleInfo = Get-Module (Get-Item $BuildInfo.ModuleManifest).FullName -ListAvailable -WarningAction SilentlyContinue -ErrorAction SilentlyContinue -ErrorVariable Problems

# If there are any problems that count, fail
if ($Problems = $Problems.Where( {$_.FullyQualifiedErrorId -notmatch $ErrorsWeIgnore})) {
foreach ($problem in $Problems) {
Write-Error $problem
}
throw "Unresolvable problems in module manifest"
if ($ModuleInfo = ImportModuleManifest $BuildInfo.SourcePath) {
# Update the module manifest with our build configuration and output it
Update-Object -InputObject $ModuleInfo -UpdateObject $BuildInfo
} else {
throw "Unresolvable problems in module manifest: '$($BuildInfo.SourcePath)'"
}

# Update the ModuleManifest with our build configuration
$ModuleInfo = Update-Object -InputObject $ModuleInfo -UpdateObject $BuildInfo
$ModuleInfo = Update-Object -InputObject $ModuleInfo -UpdateObject @{ DefaultCommandPrefix = $ModuleInfo.Prefix; Prefix = "" }

$ModuleInfo
}
9 changes: 4 additions & 5 deletions 9 Source/Private/MoveUsingStatements.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ function MoveUsingStatements {

# Avoid modifying the file if there's no Parsing Error caused by Using Statements or other errors
if (!$ParseErrors.Where{$_.ErrorId -eq 'UsingMustBeAtStartOfScript'}) {
Write-Debug "No Using Statement Error found."
Write-Debug "No using statement errors found."
return
}
# Avoid modifying the file if there's other parsing errors than Using Statements misplaced
if ($ParseErrors.Where{$_.ErrorId -ne 'UsingMustBeAtStartOfScript'}) {
Write-Warning "Parsing errors found. Skipping Moving Using statements."
Write-Warning "Parsing errors found. Skipping moving using statements."
return
}

Expand Down Expand Up @@ -71,9 +71,8 @@ function MoveUsingStatements {
)

if ($ParseErrors) {
Write-Warning "Oops, it seems that we introduced parsing error(s) while moving the Using Statements. Cancelling changes."
}
else {
Write-Warning "We introduced parsing error(s) while attempting to move using statements. Cancelling changes."
} else {
$null = Set-Content -Value $ScriptText -Path $RootModule -Encoding $Encoding
}
}
6 changes: 2 additions & 4 deletions 6 Source/Private/ResolveBuildManifest.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function ResolveBuildManifest {
if ((Split-Path $SourcePath -Leaf) -eq 'build.psd1') {
$BuildManifest = $SourcePath
} elseif (Test-Path $SourcePath -PathType Leaf) {
# When you pass the ModuleManifest as parameter, you must have the Build Manifest in the same folder
# When you pass the SourcePath as parameter, you must have the Build Manifest in the same folder
$BuildManifest = Join-Path (Split-Path -Parent $SourcePath) [Bb]uild.psd1
} else {
# It's a container, assume the Build Manifest is directly under
Expand All @@ -19,9 +19,7 @@ function ResolveBuildManifest {
# Make sure we are resolving the absolute path to the manifest, and test it exists
$ResolvedBuildManifest = (Resolve-Path $BuildManifest -ErrorAction SilentlyContinue).Path

if (-Not ($ResolvedBuildManifest)) {
throw "Couldn't resolve the Build Manifest at $BuildManifest"
} else {
if ($ResolvedBuildManifest) {
$ResolvedBuildManifest
}

Expand Down
1 change: 1 addition & 0 deletions 1 Source/Private/SetModuleContent.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ function SetModuleContent {
[string]$Encoding = $(if($IsCoreCLR) { "UTF8Bom" } else { "UTF8" })
)
begin {
Write-Debug "SetModuleContent WorkingDirectory $WorkingDirectory"
Push-Location $WorkingDirectory -StackName SetModuleContent
$ContentStarted = $false # There has been no content yet

Expand Down
2 changes: 1 addition & 1 deletion 2 Source/build.psd1
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Use this file to override the default parameter values used by the `Build-Module`
# command when building the module (see `Get-Help Build-Module -Full` for details).
@{
Path = "ModuleBuilder.psd1"
ModuleManifest = "ModuleBuilder.psd1"
# Subsequent relative paths are to the ModuleManifest
OutputDirectory = "../"
VersionedOutputDirectory = $true
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.