I'm writing a game development IDE that creates and compiles .NET projects (which I've been working on for the past few years) and am in the process of updating it to generate output not only for Windows/Visual Studio, but also for Linux/MonoDevelop (a thrillingly simple process for .NET, but still requiring some tweaks).
As part of this, I have found it necessary to start generating an app.config file as part of this to map dependent DLL names to Linux dependency names with <dllmap> elements. I'm confused about who's responsible for copying the app.config file to the output name app.exe.config. In a Visual Studio project, the Build Action for app.config seems to normally be set to "None" and its settings indicate that it won't be copied anywhere, yet when Visual Studio compiles the project it generates app.exe.config (though I've sometimes found this to be unreliable). When I use MSBuild to build a solution file generated by the IDE (for debugging purposes), MSBuild copies app.config to app.exe.config. But when I compile the project with CSharpCodeProvider.CompileAssemblyFromFile it (naturally) doesn't like the config file being included as source code ("app.config(1,1) : error CS0116: A namespace does not directly contain members such as fields or methods"), and of course it doesn't copy it to the output when I don't include it as an input. Is it my responsibility to simply copy app.config to app.exe.config independently, or is there a more standard way of doing this?
Is it hardwired to take the first *.config file? In my IDE it's conceivable that the app.config file would be renamed or another one added (just as in Visual Studio). It seems odd to me that the IDE has this secret action for config files (I think MonoDevelop behaves similarly in this regard because I couldn't find a special action for config files there either). I don't know how it even picks to what files this secret action applies.
The C# compiler does not care about the config file at all. Build environments (MSBuild and VS) will take care of copying that file themselves.
Order:
first app.config file with None build action, in the project directory
first app.config file with Content build action, in the project directory
first app.config file with None build action, in a subdirectory
first app.config file with Content build action, in a subdirectory
msbuild/xbuild also allow you to override this by setting the $(AppConfig) property.
A slightly more technical answer - your project references Microsoft.CSharp.targets via this key in the csproj file:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
This file would resolve to something like c:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets, depending on your framework version.
Inside of it you have this section which does the work:
<!--
============================================================
_CopyAppConfigFile
Copy the application config file.
============================================================
-->
<Target
Name="_CopyAppConfigFile"
Condition=" '#(AppConfigWithTargetPath)' != '' "
Inputs="#(AppConfigWithTargetPath)"
Outputs="#(AppConfigWithTargetPath->'$(OutDir)%(TargetPath)')">
<!--
Copy the application's .config file, if any.
Not using SkipUnchangedFiles="true" because the application may want to change
the app.config and not have an incremental build replace it.
-->
<Copy
SourceFiles="#(AppConfigWithTargetPath)"
DestinationFiles="#(AppConfigWithTargetPath->'$(OutDir)%(TargetPath)')"
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
Retries="$(CopyRetryCount)"
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
UseHardlinksIfPossible="$(CreateHardLinksForAdditionalFilesIfPossible)"
>
<Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
</Copy>
</Target>
The App.Config file seems to be passed as an environment variable (it is expected to be present but who sets it, I don't know):
<ItemGroup>
<AppConfigWithTargetPath Include="$(AppConfig)" Condition="'$(AppConfig)'!=''">
<TargetPath>$(TargetFileName).config</TargetPath>
</AppConfigWithTargetPath>
</ItemGroup>
Edit: For how app.config is selected, see this answer - https://stackoverflow.com/a/40293508/492336.
The handling of app.config is special, it is treated By Name, the build process will select the app.config file following this order:
Choose the value $(AppConfig) set in the main project.
Choose #(None) App.Config in the same folder as the project.
Choose #(Content) App.Config in the same folder as the project.
Choose #(None) App.Config in any subfolder in the project.
Choose #(Content) App.Config in any subfolder in the project.
I think MSBuild is responsible for copying. If you would dig trough stock .target files, then you'd probably find corresponding directives. VS by itself doesn't copy.
Note also that Visual Studio does validate the config file.
Related
I've developed an ExcelDNA Addin in C#, VS2019. Before the deployment to the customers, I would like to rename the packed xll output of the project.
Right now, my project outputs 2 files (names are just for this example):
MyAddin-packed.xll
MyAddin64-packed.xll
Now I want to change the name of the output to CoolAddin-packed.xll.
Looks like ExcelDnaBuild.exe is completely ignoring following changes I did (the output is still MyAddin-packed.xll):
Changed the Output Assembly name in the VS project.
Change the AssemblyTitle attribute in AssemblyInfo.cs
Changed the name in *.dna file that is included in the project.
<DnaLibrary Name="CoolAddinAddin" RuntimeVersion="v4.0">
<ExternalLibrary Path="CoolAddinAddin.dll" ExplicitExports="false"
LoadFromBytes="true" Pack="true" />
</DnaLibrary>
In addition, I dont see any tasks in PRE/POST build events that might be using the old name.
I am aware that the last solution might be to re-create the project skeleton and attach the files.
Any ideas?
Hope this helps someone.
The base name for the packed file is the name of .dna file that is automatically generated by NuGet Package Manager install script of Excel.Dna package. CoolAddin-Addin.dna in this case.
So in this case, building the project will create 2 files:
CoolAddin-Addin-packed.xll
CoolAddin-AddIn64-packed.xll
If you rename the CoolAddin-Addin.dna file, it will rename the base of xll packed files.
When I build a windows application. It does not work because it cannot read my app.config file. I looked in the bin directory and it does not have the appname.exe.config file. If I manually copy over the app.config and rename it the appname.exe.config the application will work..but when I build the project this file is never created automagically. What am I missing? Why is my project not creating it? I have looked through the whole folder structure and there is no other config files.
Everyone here is giving you false information I think. I think #bowlturner had it right. According to Microsoft, you do need to set the app.config's Copy to output directory property to either Copy Always or Copy if newer. From Microsoft's MSDN Site:
When you develop in Visual Studio, place the source configuration file for your app in the project directory and set its Copy To Output Directory property to Copy always or Copy if newer. The name of the configuration file is the name of the app with a .config extension. For example, an app called myApp.exe should have a source configuration file called myApp.exe.config.
Visual Studio automatically copies the source configuration file to the directory where the compiled assembly is placed to create the output configuration file, which is deployed with the app.
The correct settings are:
Build Action = None
Copy to Output Directory = Do not copy
and very important, the project file needs to contain this element:
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
The only thing that worked for me was to delete bin\ and obj\ entirely. Rebuild and the file is created fine.
Look on App.config properties, should be:
BuildAction: None
CopyToOutputDirectory: Do not copy
Custom tool and custom tool namespace should be empty
Also try to Rebuild project. Right click on project -> Rebuild
Assuming you're using Visual Studio click your config file in the Solution Explorer and and in the Properties panel set Copy To Output Directory to something other than Do Not Copy.
My .NET project depends on a bunch of native dll libraries that should be copied into the same directory as .exe file. Dll files are added to main project to "ExternalDependencies" folder and are copied to output folder upon custom post-build action.
When I publish my app with ClickOnce utility all dlls are placed into ExternalDependencies/ relative path, not in the same directory as exe.
I don't want to put the libraries into root of the project, but I have to find a way to change relative path during installation.
I found no way of doing this, except for manual manifest generation: https://msdn.microsoft.com/en-us/library/xc3tc5xx.aspx
Maybe I am missing something? Is there an easier way to do this?
Ok, figured it out. This blog post describes the hidden possibilities of project files which allow to do what I want: http://blogs.msdn.com/b/mwade/archive/2008/06/29/how-to-publish-files-which-are-not-in-the-project.aspx
Basically you should add an item to you project file for each file you will be deploying if you want a custom path for it:
<ItemGroup>
<PublishFile Include="ExternalDependencies\DSAPI.dll">
<Visible>False</Visible>
<Group>
</Group>
<TargetPath>DSAPI.dll</TargetPath>
<PublishState>Include</PublishState>
<IncludeHash>True</IncludeHash>
<FileType>File</FileType>
</PublishFile>
TargetPath property is what I was looking for. It's a shame you cannot change it from VS UI!
I'm trying to reference a C# DLL project from another solution, but the build is generating the DLL in a very strange output folder.
The directory contents is this:
c:\a\b\c\src\Solution.sln
c:\a\x\y\z\MyDLL\MyDLL.csproj
The MyDLL.csproj does not have an <OutputPath> tag. It does however have a <SolutionDir> tag that I don't see often.
The computed output path, as shown in Properties view, happens to be:
..\..\..\..\b\c\src-z\MyDLL\objd\i386
This corresponds to this path:
c:\a\b\c\src-z\src\MyDLL\objd\i386
which is very weird, since I'm not aware of anything in configuration with src-z. Is Visual Studio computing a path with hyphens?
I want to fix this, possibly changing the <SolutionDir>, but I don't want to break other solutions.
The computation seems to happen very early in the build process, as the first thing the builder logs is:
1>Project 'MyDLL (x\y\z\MyDLL\MyDLL.csproj)' is not up to date.
Input file 'x\y\z\MyDLL\MyDLL.csproj' is modified after output
file 'c:\a\b\c\src-z\src\MyDLL\objd\i386\MyDLL.pdb'.
So what algorithm is Visual Studio using to compute the output path when a project <OutputPath> tag is not found?
The code that populates OutputPath can be found in C:\Program Files (x86)\MSBuild\12.0\Bin\Microsoft.Common.CurrentVersion.targets (by default; it may change if the MSBuild install directory is different).
There is a comment about it that reads:
OutDir:
Indicates the final output location for the project or solution. When building a solution, OutDir can be used to gather multiple project outputs in one location. In addition, OutDir is included in AssemblySearchPaths used for resolving references.
OutputPath:
This property is usually specified in the project file and is used to initialize OutDir. OutDir and OutputPath are distinguished for legacy reasons, and OutDir should be used if at all possible.
So while OutputPath is often what is referenced, it is OutDir that actually matters. If there is no set platform or configuration then OutputPath is set to bin\Debug\.
If we look in that file we can see the logic that sets OutDir is pretty simple. If OutDir isn't set then it is set to OutputPath. There is a little bit of extra logic around appending a folder named after the project to the path if GenerateProjectSpecificOutputPath is set.
Looking at Microsoft.CSharp.targets, Microsoft.CSharp.CurrentVersion.targets, Microsoft.Common.Targets and Microsoft.Common.CurrentVersion.targets, neither OutDir nor OutputPath appears to be set anywhere else. So, assuming an "out of the box" C# project, it basically will equal either OutDir, OutputPath, or bin\Debug.
The last bit of information that is relevant is the working directory. OutDir can be a relative path in which case it will be somewhere in the working directory.
As for the BaseIntermediateOutputPath, the information is in the same file:
BaseIntermediateOutputPath:
This is the top level folder where all configuration specific intermediate output folders will be created. Default value is obj\
IntermediateOutputPath:
This is the full intermediate Output Path, and is derived from BaseIntermediateOutputPath, if none specified (eg. obj\debug). If this property is overridden, then setting BaseIntermediateOutputPath has no effect.
Looking at the logic, it defaults to obj\ if not set (note, relative path so the working directory comes into play again).
I would say you have an OutputPath declaration somewhere but if not in your .csproj file in one of the imported targets-files.
I will split my answer in several parts:
<SolutionDir> tag and changing it.
With this tag you can declare the solution directory variable $(SolutionDir), but I don't think it is used by Visual Studio from inside the .csproj file if it is different from where the solution actually resides.
But the tag could be used by MSBuild builds. I couldn't get Visual Studio to use it if I declared it in the .csproj file.
Visual Studio uses the directory of the loaded solution as variable content instead of any different declaration in the .csproj file from my tests.
Having said that: changing the tag will probably change the use of $(SolutionDir) variable in any places where you use it like PreBuild/PostBuild or Output directories. Whereever it is used. But not in Visual Studio.
If it is not used changing it won't change much in Visual Studio builds.
No OutputPath tag
I would expect this tag if the .csproj file is loaded inside Visual Studio. But it is possible to declare it in targets-files instead of .csproj files. So you have to check the imported targets files as well.
e.g. Look at the file Microsoft.CSharp.targets. This is the standard imported targets file for CS-Projects in Visual Studio. It makes computations and declarations of OutputPath.
Second possibility to look are your own targets files that you can import (handwritten .csproj files). Declarations are possible there as well.
Third possibility: I think it is possible to declare this variable via command line argument for MSBuild though not for Visual Studio.
Would Visual Studio work without this tag in the .csproj file?
Usually not, BUT you could declare to override errors from the standard C# targets file (Microsoft.CSharp.targets) and make it build - but then it is declared in this file.
Standard Output directory if not declared would be \bin\Debug (for debug builds) as defined by Microsoft.CSharp.targets.
Otherwise you get an error. Missing OutputPath declaration.
How is it computed
Look at the .csproj file and targets files (excluding command line parameters in MSBuild) and see which PropertyGroup sections are valid (check the conditions).
For example:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
</PropertyGroup>
Everything inside this would be valid for the selected configuration Debug and AnyCPU. Especially in the standard targets file there are a some declarations for OutputPath.
This is where it seems to get non-standard for you if your information is correct: Usually you get an error in Visual Studio building if OutputPath is not declared because there is a section
in targets checking the declaration and throwing an error (you can change this behavior - see point 3). Interpretation would be top-down like any script.
src-z comes from where? (in Visual Studio)
Scan all .csproj files and targets files you are using for "src-z" and the hard disk location of the solution file to find out where it comes.
If you really don't have any OutputPath declaration anywhere then I don't have an explanation why your VS-Build is working.
And if you don't find src-z then can you post some more information on your .csproj file and how you use it (Visual Studio and solutions)?
I found the issue. One of the referenced projects was defining <BaseIntermediateOutputPath> as $SolutionDir + "-" + $ProjectName, which yields to c:\a\b\c\src-z.
As for answering my question, the best I could deduce is:
If there is <OutputPath>, then it's used.
Otherwise, the output path is defined as <BaseIntermediateOutputPath> + <IntermediateOutputPath> + "bin".
The default value of <BaseIntermediateOutputPath> is the project directory.
The default value of <IntermediateOutputPath> is defined by the build type (i.e., Debug, Release, etc.).
In our C# project we use Spring as an Inversion of Control (IoC) container. We noticed, that changes to the Spring configuration xml file do not cause the solution to be rebuild. Therefore the changed configuration is not copied to the output folder and every debug-run uses the old configuration.
How can we force Visual Studio (2008) to copy the config file even though none of the project code has changed?
Further info:
The build action of the config file is set to None. Copy to Output Directory is set to Copy always.
Seems the solution is simpler than I thought. If the build action for the *.xml config file is changed to Embedded Resource, then changing the file also touches the project. Therefore, every debug cycle causes a rebuild of the project, where the config file is included. This causes the file to be copied to the output directory with the adjusted configuration. => Problem solved.