17

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.

Jon Limjap
  • 94,284
  • 15
  • 101
  • 152
BlueMonkMN
  • 25,079
  • 9
  • 80
  • 146
  • To clarify, my question is, since I am using CSharpCodeProvider to compile the code (without resorting to a shell command such as MSBuild to compile the project), what is the correct way to get the app.exe.config in the output? – BlueMonkMN Mar 30 '09 at 14:56
  • I'd suggest using MSBuild in your IDE and create MSBuild tasks for any special build output you generate, which MSBuild has no support for. This would enable MSBuild to compile to your solutions. In addition this would make your product easier to integrate with continous integration like TeamCity. – grover Mar 30 '09 at 15:29
  • MSBuild can already compile my solutions. And when it does, it is already handling app.config correctly. CSharpCodeProvider seems like a more direct solution with less overhead than shelling out to MSBuild. The IDE generates a solution file, but does not use it when compiling internally. – BlueMonkMN Mar 30 '09 at 16:53
  • see http://stackoverflow.com/questions/5994244 – hortman Nov 29 '11 at 16:28

5 Answers5

8

Order:

  1. first app.config file with None build action, in the project directory
  2. first app.config file with Content build action, in the project directory
  3. first app.config file with None build action, in a subdirectory
  4. first app.config file with Content build action, in a subdirectory

msbuild/xbuild also allow you to override this by setting the $(AppConfig) property.

radical
  • 4,364
  • 2
  • 25
  • 27
  • Your mention of the `$(AppConfig)` property was really helpful to me. I feel like you could improve this answer with some more background on where you found this information though. I found some extra explanations [here](https://timvw.be/2008/03/17/easily-switching-between-appconfig-files-with-msbuild/) but if there is an official link that would be even better. If not, just a mention of the `targets` file would be welcome as well. – julealgon Dec 12 '17 at 20:16
8

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.

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • 1
    My IDE is not using MSBuild to compile the project. It is using CSharpCodeProvider. So what should my IDE be doing? – BlueMonkMN Mar 30 '09 at 14:54
  • It should copy the config file using `File.Copy` after compilation. – Mehrdad Afshari Mar 30 '09 at 14:57
  • Copy the first *.config file to outputname.exe.config and ignore any possible other *.config file (which should not exist)? – BlueMonkMN Mar 30 '09 at 14:59
  • I'd copy `app.config` to `outputname.exe.config` and ignore other config files. – Mehrdad Afshari Mar 30 '09 at 15:00
  • IMHO, you should use MSBuild to compile the project. That is what Visual Studio does (it does not do the compile itself). There are a number of managed classes available to you to invoke MSBuild programmatically. – Erv Walter Mar 30 '10 at 13:30
  • @ErvWalter Does MSBuild even exist in Linux? – BlueMonkMN Apr 07 '14 at 15:10
  • This may be too late for @BlueMonkMN, but there is XBuild (From the Mono project) for Linux. If you're using dotnet core, then the toolchain can do the build for you in a platform agnostic manner – Sudhanshu Mishra Feb 08 '18 at 01:10
5

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.
sashoalm
  • 75,001
  • 122
  • 434
  • 781
1

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.

XOR
  • 2,177
  • 1
  • 17
  • 15
1

Note also that Visual Studio does validate the config file.

Rob
  • 4,210
  • 3
  • 30
  • 25