2

Using Invoke-Build I am trying to create dynamic build tasks, an example of the approach is below. Where, in a similar fashion to the classic UNIX make facility an output file will be generated for each input file iff the input file is newer than the output file.

It accomplishes this by dynamically generating a set of incremental tasks and then calling the tasks.

The generation of the incremental tasks is outlined below:

    $Jobs = Foreach($_ in $Conf ) {
        $SourceFile = Get-Item $_
        $FileName = $SourceFile.Name
        $ObjectName = $FileName -replace '^Thing-(.*)\.conf\.ps1$','$1'
        $TaskName = "Task-$ObjectName"
        $OutFile = "Thing-$ObjectName.ps1"
        # Import the script, overwriting the "$MetaData" variable    
        $MetaData = . $SourceFile

    $TaskCode = @'
.\BuildThing.ps1
BuildThing -MetaData $MetaData
'@
        Write-Verbose "$SourceFile $OutFile"
        $SBInputs = [scriptblock]::Create(  { $SourceFile } ).GetNewClosure()
        $SBOutputs = [scriptblock]::Create( { $OutFile } ).GetNewClosure()
        $SBMain = [scriptblock]::Create($TaskCode).GetNewClosure()
        Task "$TaskName" -Inputs $SBInputs -Outputs $SBOutputs ($SBMain)

        Write-Output $TaskName
    }

This does appear to work well apart from the scoping of required other functions. No matter where I try to source the BuildThing.ps1 script when the task Task-Thing- is executed Invoke-Build complains that BuildThing is not recognized as:

"the name of a cmdlet, function, script file or operable program."

If I dot source the relevant files before running Invoke-Build everything works as desired.

This seems like a scoping problem - but it's not clear how to resolve it in a straightforward manner.

If I try to include BuildThing.ps1 outside of the loop in a static way, it does seem to be called, as confirmed by debugging, but does not appear to be in scope to be executed by the dynamic task.

Roman Kuzmin
  • 40,627
  • 11
  • 95
  • 117
user1383092
  • 507
  • 1
  • 7
  • 17
  • The problem is about `BuildThing`. Two questions. 1) What is the location of `BuildThing.ps1`? 2) What is the command `BuildThing` in the second line of `$TaskCode` (perhaps it is not found)? Should it be `.\BuildThing.ps1 -MetaData ...`? – Roman Kuzmin Feb 17 '15 at 20:12
  • Also, what is the full / exact error message? Does it contain a hint to the line of code where the error happens? Also, try to examine the content of the variable `$Error`. It contains all errors, sometimes it gives more information. – Roman Kuzmin Feb 17 '15 at 20:20
  • I think the single quotes are OK. if I initially dot source .\BuildGetter.ps1, which defines the function I want to use, before doing Invoke-Build that works fine in the single quote case; not in the double quote case. In that instance there's also no need for me to have .\BuildGetter,ps1 in the '$TaskCode =' assignment. If, on the other hand, I use double quotes then I don't think $MakeGetter is captured correctly when building the closure. Frankly I'm not even sure that's right! Learning in the open here! BuildGetter.ps1 defines a func. that takes a hashtable and outputs a file. – user1383092 Feb 17 '15 at 20:30
  • 2) It should not be .\BuildThing.ps1 -Metadata - .\BuildThing.ps1 simply defines the function BuildThing. – user1383092 Feb 17 '15 at 20:31
  • Yes, I already realized that single quotes is OK, you are using closures. It's a tricky case and I do not have yet enough info, I do not see the whole picture. Try to get all the details including all errors exactly. – Roman Kuzmin Feb 17 '15 at 20:35
  • Ah! If `.\BuildThing.ps1` defines the function `BuildThing` then you should dot-source it, i.e. call it like this: `. .\BuildThing.ps1` (dot, space, the path). – Roman Kuzmin Feb 17 '15 at 21:11
  • I literally just figured that out! :) Feeling a bit dim at the moment! – user1383092 Feb 17 '15 at 21:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/71115/discussion-between-user1383092-and-roman-kuzmin). – user1383092 Feb 17 '15 at 21:26

1 Answers1

1

With regard to the above, the appropriate approach appears to be to modify the definition of $TaskCode to dot source the file.

If we assume .\BuildThing.ps1 contains a function - e.g.

function BuildThing {
param(...)
...
}

Then the dynamic task code needs to dot source this code. This seems inefficient for a large set of similar build tasks, however it does resolve the issue.

$TaskCode = @'
. .\BuildThing.ps1
BuildThing -MetaData $MetaData
'@
user1383092
  • 507
  • 1
  • 7
  • 17
  • 1
    A remark about the original code. I think this will do: `$SBInputs = $SourceFile.FullName` and `$SBOutputs = ` instead of creating script blocks with expensive closures. – Roman Kuzmin Feb 18 '15 at 17:47