Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

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.

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.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top