Due to the extreme amount of .resx files in our application, I have created the following MSBuild script to compile all language .resx files into .resource, then embed them into satellite resource assemblies.
<Project DefaultTargets="Main" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Res Include = "Dialog\*.ja-JP.resx">
<Culture>ja-JP</Culture>
<Project>Dialog</Project>
</Res>
</ItemGroup>
<Target Name="Main">
<Message Text="$(destination)"/>
<CallTarget Targets="CompileResources" />
<CallTarget Targets="BuildSatelliteAssemblies" />
<CallTarget Targets="CopyToFolder" Condition="$(destination)!=''"/>
<CallTarget Targets="CleanUp" />
</Target>
<Target Name="CompileResources">
<GenerateResource Sources="#(Res)" PublicClass="true" >
<Output ItemName="Resources" TaskParameter="OutputResources"/>
</GenerateResource>
</Target>
<Target Name="BuildSatelliteAssemblies" DependsOnTargets="CompileResources">
<MakeDir Directories="%(Res.Culture)"/>
<AL OutputAssembly="%(Culture)\%(Project).resources.dll"
Version="0.0.0.0"
Culture="%(Culture)"
ProductName="%(Project)"
Title="%(Project)"
EmbedResources="#(Resources)"/>
</Target>
<Target Name="CopyToFolder" DependsOnTargets="BuildSatelliteAssemblies">
<MakeDir Directories="$(destination)\%(Res.Culture)"/>
<CreateItem Include="%(Res.Culture)\*.dll" AdditionalMetadata="Culture=%(Res.Culture)">
<Output ItemName="SatelliteAssemblies" TaskParameter="Include"/>
</CreateItem>
<Copy DestinationFolder="$(destination)\%(Culture)"
SourceFiles="#(SatelliteAssemblies)" />
</Target>
<Target Name="CleanUp">
<Delete Files="#(Resources)"/>
</Target>
</Project>
The satellite assemblies seem to compile and embed correctly however when I place them with my application, they are not recognized and it defaults back to the default culture resources. If I build the project with Visual Studio and use the assemblies it creates with that, they load fine.
I must be missing something in causing the application to recognize my externally built assemblies. They are all named the same and the sizes are nearly the same.
In the AL target, you can set an internal namespace to be used by each resource file in the assembly. Setting the correct namespace allowed the application to correctly find the resources in the assemblies.
Related
Every time we need to goto ClientApp folder and type npm install manually.
But in different projects - Visual Studio template project it this build happens out of box.
What configuration are required?
What I tried: I did install following nuget package: But id didn't help either.
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="4.5.3">
Create a new project in Visual Studio using the ASP.NET Core app with Angular template (learn.microsoft.com/en-us/visualstudio/javascript/tutorial-asp-net-core-with-angular?view=vs-2022), then search for the "npm" string in its .csproj files. You'll find something like this:
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" />
<ResolvedFileToPublish Include="#(DistFiles->'%(FullPath)')" Exclude="#(ResolvedFileToPublish)">
<RelativePath>wwwroot\%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
I need some help writing a post build event that would work cross platform. The following in my csproj file will work on windows but not Unix. Thanks.
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="copy /Y "$(TargetDir)bin\*.dll" "$(TargetDir)*.dll"" />
</Target>
For this specific case, it might be easier to use the MSBuild Copy Task.
In your csproj file:
<ItemGroup>
<MySourceFiles Include=$(TargetDir)\bin\*.dll"/>
</ItemGroup>
<Target Name="CopyFiles">
<Copy
SourceFiles="#(MySourceFiles)"
DestinationFolder="$(TargetDir)"
/>
</Target>
I've built a NuGet package that packages managed and unmanaged dependencies, some of which are runtime dependencies, and so I have several Targets in my .targets file for the NuGet file which copy those dependencies to the bin folder of the project consuming the NuGet package, and I'd like for the consumer to not have to insert the CallTarget nodes in their .csproj file manually.
My package's .targets file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopyDeviceIntegrationNativeDependencies">
<Message Text="Copying Device Integration native dependencies to $(OutDir)..." Importance="high" />
<ItemGroup>
<TDDeviceIntegrationNativeDependencies Include="$(MSBuildProjectDirectory)\..\Packages\TDDeviceIntegration*\lib\native\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(TDDeviceIntegrationNativeDependencies)" DestinationFiles="#(TDDeviceIntegrationNativeDependencies->'$(OutDir)\%(Filename)%(Extension)')" ></Copy>
</Target>
<Target Name="CopyDeviceIntegrationContentDependencies">
<Message Text="Copying Device Integration content dependencies to $(OutDir)..." Importance="high" />
<ItemGroup>
<TDDeviceIntegrationContentDependencies Include="$(MSBuildProjectDirectory)\..\Packages\TDDeviceIntegration*\content\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(TDDeviceIntegrationContentDependencies)" DestinationFiles="#(TDDeviceIntegrationContentDependencies->'$(OutDir)\%(Filename)%(Extension)')" ></Copy>
</Target>
<Target Name="CopyDeviceIntegrationManagedDependencies">
<Message Text="Copying Device Integration managed dependencies to $(OutDir)..." Importance="high" />
<ItemGroup>
<TDDeviceIntegrationManagedDependencies Include="$(MSBuildProjectDirectory)\..\Packages\TDDeviceIntegration*\lib\*.*" />
</ItemGroup>
<Copy SourceFiles="#(TDDeviceIntegrationManagedDependencies)" DestinationFiles="#(TDDeviceIntegrationManagedDependencies->'$(OutDir)\%(Filename)%(Extension)')"></Copy>
</Target>
</Project>
My consuming .csproj file needs to have this in it for those copies to happen:
<Target Name="AfterBuild">
<CallTarget Targets="CopyDeviceIntegrationNativeDependencies" />
<CallTarget Targets="CopyDeviceIntegrationContentDependencies" />
<CallTarget Targets="CopyDeviceIntegrationManagedDependencies" />
</Target>
Considering the fact that there can only be one AfterBuild target in this .csproj file (as far as I know), is there anyway that I can insert those CallTarget nodes for the consumer upon installation of the NuGet package so that the consumer doesn't have to add those in manually? I've never run across a NuGet package that forces the consumer to have to make changes to their .csproj file after installation.
Thanks!
You can make all of the targets hook into the build themselves, not requiring any changes by project authors:
<Target Name="Foo" AfterTargets="AfterBuild"> …
Note that it looks like you just want to include files to the output directory, which can be done without targets as static item groups:
<ItemGroup>
<Content Include="$(MSBuildThisFileDirectory)path\relativetotargetsfile\somecontent\**"
Link="\%(Filename)%(Extension)"
Visible="false"
CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
This will support incremental build. Change the Link metadata if you need the content to be put into subdirectories.
How can I include a file in the list of files in solution explorer without including it as a dependency for compilation?
I have a .targets file that generates .cs files, similar to the examples in this answer.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CoreCompileDependsOn>$(CoreCompileDependsOn);GenerateCode</CoreCompileDependsOn>
</PropertyGroup>
<ItemGroup>
<Sources Include="..\sources\*.txt" />
</ItemGroup>
<Target Name="GenerateCode" Inputs="#(Sources)" Outputs="#(Sources->'generated\%(Filename).cs')">
<!-- run executable that generates files -->
<ItemGroup>
<Compile Include="generated\*.cs" />
</ItemGroup>
</Target>
</Project>
This builds correctly and consecutive builds don't rebuild the project unnecessarily. The resulting .cs files are not visible in the solution explorer. The generated code also isn't found by intellisense.
If I add the files with ItemGroups in the .csproj, the generated files are visible in the solution explorer, but subsequent builds result in rebuilding the project unnecessarily. The genereated code still isn't found by intellisense.
<ItemGroup>
<Sources Include="..\sources\*.txt">
<Link>sources\%(Filename)%(Extension)</Link>
</Sources>
<!-- using None instead of Compile on the next line makes no difference -->
<Compile Include="#(Sources->'generated\%(Filename).cs')">
<Generator>MSBuild:Compile</Generator>
<Link></Link>
</Compile>
</ItemGroup>
How can I tell msbuild that the .cs files included in the project are inconsequential to the build and therefore shouldn't trigger rebuilding the entire project?
Move the code generation to BeforeCompile instead of CoreCompileDependsOn. this will keep the generation of the files from tirggering the subsequent builds.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="BeforeCompile" DependsOnTargets="GenerateCode">
</Target>
<Target Name="GenerateCode" Inputs="#(Sources)" Outputs="#(Sources->'generated\%(Filename).cs')">
<!-- run executable that generates files -->
</Target>
</Project>
If you include all of the generated files in the .csproj, the visual studio intellisense will work.
<ItemGroup>
<Sources Include="..\sources\*.txt">
<Link>sources\%(Filename)%(Extension)</Link>
<LastGenOutput>generated\%(Filename).cs</LastGenOutput>
</Sources >
<Compile Include="#(Sources->'generated\%(Filename).cs')">
<Link></Link>
</Compile>
</ItemGroup>
I'm trying to use MSBuild to compile my ASP.NET MVC3 applicaiton. Since DLL's don't require a Main method and I have specified that the target is a Library, why is the compiler throwing the following exception:
CSC : error CS5001: Program 'c:\MvcApplication1\web\bin\MvcApplication1.dll' does not contain a static 'Main' method suitable for an entry point[C:\MvcApplication1\web\MvcApplication1.csproj]
Here's the .csproj file:
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputType>Library</OutputType>
<AssemblyName>MvcApplication1</AssemblyName>
<OutputPath>bin\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="*.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="..\lib\*.dll" />
</ItemGroup>
<Target Name="Build">
<MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />
<Csc References="#(Reference)" Sources="#(Compile)" OutputAssembly="$(OutputPath)$(AssemblyName).dll" />
<Copy SourceFiles="#(Reference)" DestinationFolder="$(OutputPath)" />
</Target>
</Project>
Csc should have a TargetType of library. The default is supposed to be Library (see MSDN below) although in this case it doesn't seem to be the case.
Change you <Csc step as follows:
<Csc TargetType="library" References="#(Reference)" .... />
From MSDN re TargetType:
Specifies the file format of the output file. This parameter can have
a value of library, which creates a code library, exe, which creates a
console application, module, which creates a module, or winexe, which
creates a Windows program. The default value is library. For more
information, see /target (C# Compiler Options).