VS2015 MSBuild项目上下文中的Powershell变量作用域和临时文件 问题:

VS2015 MSBuild项目上下文中的Powershell变量作用域和临时文件 问题:,powershell,msbuild,Powershell,Msbuild,两个独立的Powershell脚本,从两个独立的项目(Chrome和Firefox)运行,显然写入同一个临时文件 这种情况并不经常发生,显然是每隔几个月左右发生一次。但是,当它发生时,这两个独立的项目都以它们的manifest.json和package.json文件组合在一起结束,这样每个文件都会有来自另一个文件的随机行散布在其中 你知道为什么会这样吗 细节 我有一个VS2015解决方案,其中包含25个项目。其中一个项目称为“部署”,并声明了对多个其他项目的依赖关系。这个问题的两个重要依赖项是“

两个独立的Powershell脚本,从两个独立的项目(Chrome和Firefox)运行,显然写入同一个临时文件

这种情况并不经常发生,显然是每隔几个月左右发生一次。但是,当它发生时,这两个独立的项目都以它们的
manifest.json
package.json
文件组合在一起结束,这样每个文件都会有来自另一个文件的随机行散布在其中

你知道为什么会这样吗

细节 我有一个VS2015解决方案,其中包含25个项目。其中一个项目称为“部署”,并声明了对多个其他项目的依赖关系。这个问题的两个重要依赖项是“ChromeExtension”和“FirefoxAddOn”项目

每个项目都包含一个构建目标,用于在适当的清单文件中设置版本信息。以下是Chrome的目标:

<Target Name="SetVersion">
  <PropertyGroup>
    <PowerShellExe>powershell.exe</PowerShellExe>
    <PreBuildScript>$(SolutionDir)powershell\ChromePreBuild.ps1</PreBuildScript>
  </PropertyGroup>
  <Exec Command="$(PowerShellExe) -NonInteractive -ExecutionPolicy Unrestricted -command &quot;&amp; { &amp;'$(PreBuildScript)' -solutionDir '$(SolutionDir)\' }&quot;" />
</Target>
Firefox脚本:

param($solutionDir)

. "${solutionDir}powershell\BuildConfig.ps1"
. "${solutionDir}powershell\ReplaceVersion.ps1"

Write-Host "Performing pre-build actions for Firefox add-on $firefoxExtensionVersion"

ReplaceVersion "${solutionDir}FirefoxAddOn\package.json" $firefoxExtensionVersion
从这两个脚本调用的
ReplaceVersion
函数非常难看,但它完成了任务:

function ReplaceVersion {
    $file = $args[0]
    $replacementVersion = $args[1]
    Write-Host "- Updating version number in $file to ""$replacementVersion"""
    $tmp = [System.IO.Path]::GetTempFileName();
    if(Test-Path $tmp -PathType Leaf) {
        Remove-Item $tmp
    }
    Get-Content $file | Foreach-Object -process {
        $line = $_
        if ( ($line -match 'AssemblyVersion.*(\d+\.\d+\.\d+\.\d+)') -or
             ($line -match 'AssemblyFileVersion.*(\d+\.\d+\.\d+\.\d+)') -or
             ($line -match 'AppFolderName.*(\d+\.\d+\.\d+\.\d+)') -or
             ($line -match 'SupportedSyncFeatureSetRevision\s*=\s*(\d+);') -or
             ($line -match 'this.AddinName = ".* v(\d+\.\d+\.\d+\.\d+)";') -or
             ($line -match 'this.Text = ".* v(\d+\.\d+\.\d+\.\d+)";') -or
             ($line -match '"version":.*"(\d+\.\d+\.\d+\.?\d*)"') ) {
             $version = $matches[1]
             $line = $line -replace "$version", "$replacementVersion"
        }
        $line | Add-Content $tmp
    }
    #replace the old file with the new one
    Remove-Item -force $file
    Move-Item $tmp $file -Force -Confirm:$false
}
ReplaceName
功能(仅限Chrome)是完全相同的,但要匹配的模式不同:

function ReplaceName {
    $file = $args[0]
    $replacementName = $args[1]
    Write-Host "- Updating extension name in $file to ""$replacementName"""
    $tmp = [System.IO.Path]::GetTempFileName();
    if(Test-Path $tmp -PathType Leaf) {
        Remove-Item $tmp
    }
    Get-Content $file | Foreach-Object -process {
        $line = $_
        if ( ($line -match '"name":.*"(.*)"') ) {
             $name = [regex]::escape($matches[1])
             $line = $line -replace "$name", "$replacementName"
        }
        $line | Add-Content $tmp
    }
    #replace the old file with the new one
    Remove-Item -force $file
    Move-Item $tmp $file -Force -Confirm:$false
}

您应该从脚本中删除该部分:

if(Test-Path $tmp -PathType Leaf) {
    Remove-Item $tmp
}
如中所述

在磁盘上创建一个唯一命名的零字节临时文件,并返回该文件的完整路径

[System.IO.Path]::GetTempFileName()
将为您创建空文件,因此您不必删除并重新创建它

如果查看
[System.IO.Path]:GetTempFileName()
,则可以看到它是通过调用WinAPI函数实现的,其中
uUnique
等于零。以下是文件的相关部分:

如果
uUnique
为零,则函数将尝试使用当前系统时间形成唯一的文件名。如果该文件已存在,则该数字将增加1,并且函数将测试该文件是否已存在。直到找到唯一的文件名为止;函数使用该名称创建一个文件并将其关闭


如您所见,
GetTempFileName
使用文件存在作为创建不同文件名的信号。通过删除文件,您打开了一种可能性,即后续调用将为临时文件返回相同的文件名。

从脚本中删除该部分
if(测试路径$tmp-PathType-Leaf){Remove Item$tmp}
.Ha!谢谢我打赌这就是原因。我不知道为什么会在里面。从文件的第一个版本开始就是这样的,所以这行代码没有历史记录。我猜想这意味着
GetTempFileName()
确保了基于
Temp
目录内容的唯一性,而不是基于任何外部源?我无法确定测试以确保这是解决方案,因为问题很少发生,我不知道如何强制它发生。但我很肯定你是对的。如果你想写一个问题的答案,我会接受的。
if(Test-Path $tmp -PathType Leaf) {
    Remove-Item $tmp
}