I'm writing a simple extension to allow MSBuild to compile solidity source files.
My goal is when my solidproj
gets compiled it runs specific executable which produces several bin
and abi
files that should be copied to the target directory. That sounds easy, however in practice it isn't.
I currently have following targets:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.CurrentVersion.targets" />
<Target Name="CreateManifestResourceNames" />
<Target Name="CoreCompile">
<Exec Command='$(RunCommand) "-o @(OutDir) @(SolCompile)" $(RunCommandArguments)' WorkingDirectory="$(RunWorkingDirectory)" />
</Target>
</Project>
It almost works except that it expect that single exe
file gets compiled
<!-- Copy the build product (.dll or .exe). -->
<Copy
SourceFiles="@(IntermediateAssembly)"
DestinationFolder="$(OutDir)"
SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
Retries="$(CopyRetryCount)"
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)"
UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)"
Condition="'$(CopyBuildOutputToOutputDirectory)' == 'true' and '$(SkipCopyBuildProduct)' != 'true'"
>
<Output TaskParameter="DestinationFiles" ItemName="MainAssembly"/>
<Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
</Copy>
Error MSB3030 Could not copy the file "obj\Debug\Solidity9.exe" because it was not found. Solidity9 C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets 4118
How can I ask it to copy everything of this spefic extension to the output?
The whole project is on github here.
I ended up with creating all build steps from scratch:
<ItemGroup>
<CopyUpToDateMarker Include="$([MSBuild]::NormalizePath('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', '$(MSBuildProjectFile).CopyComplete'))" />
</ItemGroup>
<UsingTask
TaskName="GetOutdatedFiles"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<FileList ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<UpToDateMarker ParameterType="System.String" Required="true" />
<Result ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
var modifiedFiles = new List<ITaskItem>();
if(!File.Exists(UpToDateMarker))
{
Result = FileList;
}
else
{
DateTime markerTimeStamp = File.GetLastWriteTime(UpToDateMarker);
Result = FileList.Where(file => DateTime.Parse(file.GetMetadata("ModifiedTime")) > markerTimeStamp).ToArray();
}
]]>
</Code>
</Task>
</UsingTask>
<Target Name="Build" DependsOnTargets="CoreBuild;AfterBuild" />
<Target Name="CoreBuild">
<Error Condition="'$(OutDir)' == ''" Text="The OutDir property is missing" />
<Error Condition="'$(IntermediateOutputPath)' == ''" Text="The IntermediateOutputPath property is missing" />
<GetOutdatedFiles FileList="@(SolCompile)" UpToDateMarker="@(CopyUpToDateMarker)">
<Output TaskParameter="Result" ItemName="ChangedFiles"/>
</GetOutdatedFiles>
<Exec Command='$(RunCommand) "%(ChangedFiles.Identity)" -o "$(IntermediateOutputPath)" $(RunCommandArguments)' WorkingDirectory="$(RunWorkingDirectory)" Condition="'@(ChangedFiles)' != ''"/>
</Target>
<Target Name="AfterBuild">
<ItemGroup>
<BinFiles Include="$(IntermediateOutputPath)/*.bin"/>
<AbiFiles Include="$(IntermediateOutputPath)/*.abi"/>
</ItemGroup>
<Copy SourceFiles="@(BinFiles)" DestinationFolder="$(OutDir)" SkipUnchangedFiles="true">
<Output TaskParameter="CopiedFiles" ItemName="BinFilesCopiedInThisBuild"/>
</Copy>
<Copy SourceFiles="@(AbiFiles)" DestinationFolder="$(OutDir)" SkipUnchangedFiles="true">
<Output TaskParameter="CopiedFiles" ItemName="AbiFilesCopiedInThisBuild"/>
</Copy>
<Touch Files="@(CopyUpToDateMarker)" AlwaysCreate="true" Condition="'@(BinFilesCopiedInThisBuild)'!='' or '@(AbiFilesCopiedInThisBuild)'!=''" />
</Target>
<Target Name="Clean">
<Delete Files="@(CopyUpToDateMarker)" />
<RemoveDir Directories="$(OutDir)" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
Still need solve problems with transitive dependencies, but it's another question