我可以从 C# 中的编译时环境变量创建常量吗?

发布于 2024-10-07 09:50:59 字数 471 浏览 3 评论 0原文

我们使用 Hudson 来构建我们的项目,Hudson 可以方便地定义环境变量,例如 %BUILD_NUMBER% 在编译时。

我想在代码中使用该变量,这样我们就可以做一些事情,例如在运行时记录它的构建内容。但是我不能System.Environment.GetEnvironmentVariable,因为这是访问运行时环境,我想要的是这样的:

#define BUILD_NUM = %BUILD_NUMBER%

或者:

const string BUILD_NUM = %BUILD_NUMBER%

除非我不知道语法。有人可以指出我正确的方向吗?谢谢!

We use Hudson to build our projects, and Hudson conveniently defines environment variables like %BUILD_NUMBER% at compile time.

I'd like to use that variable in code, so we can do things like log what build this is at run time. However I cannot do System.Environment.GetEnvironmentVariable because that is accessing the run-time environment, what I want is something like:

#define BUILD_NUM = %BUILD_NUMBER%

Or:

const string BUILD_NUM = %BUILD_NUMBER%

Except I don't know the syntax. Can someone please point me in the right direction? Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

抱着落日 2024-10-14 09:50:59

好吧,这就是我最后要做的事情。它不是很优雅,但是很有效。我创建了一个如下所示的预构建步骤:

echo namespace Some.Namespace > "$(ProjectDir)\CiInfo.cs"
echo { >> "$(ProjectDir)\CiInfo.cs"
echo     ///^<summary^>Info about the continuous integration server build that produced this binary.^</summary^> >> "$(ProjectDir)\CiInfo.cs"
echo     public static class CiInfo >> "$(ProjectDir)\CiInfo.cs"
echo     { >> "$(ProjectDir)\CiInfo.cs"
echo         ///^<summary^>The current build number, such as "153"^</summary^> >> "$(ProjectDir)\CiInfo.cs"
echo         public const string BuildNumber = ("%BUILD_NUMBER%" == "" ? @"Unknown" : "%BUILD_NUMBER%"); >> "$(ProjectDir)\CiInfo.cs"
echo         ///^<summary^>String of the build number and build date/time, and other useful info.^</summary^> >> "$(ProjectDir)\CiInfo.cs"
echo         public const string BuildTag = ("%BUILD_TAG%" == "" ? @"nohudson" : "%BUILD_TAG%") + " built: %DATE%-%TIME%"; >> "$(ProjectDir)\CiInfo.cs"
echo     } >> "$(ProjectDir)\CiInfo.cs"
echo } >> "$(ProjectDir)\CiInfo.cs"

然后我将“CiInfo.cs”添加到项目中,但从版本控制中忽略它。这样我就不必编辑或提交它,并且项目始终有一个可用的常量,即最新的版本号和时间。

Okay here's what I wound up doing. It's not very elegant, but it works. I created a pre-build step that looks like this:

echo namespace Some.Namespace > "$(ProjectDir)\CiInfo.cs"
echo { >> "$(ProjectDir)\CiInfo.cs"
echo     ///^<summary^>Info about the continuous integration server build that produced this binary.^</summary^> >> "$(ProjectDir)\CiInfo.cs"
echo     public static class CiInfo >> "$(ProjectDir)\CiInfo.cs"
echo     { >> "$(ProjectDir)\CiInfo.cs"
echo         ///^<summary^>The current build number, such as "153"^</summary^> >> "$(ProjectDir)\CiInfo.cs"
echo         public const string BuildNumber = ("%BUILD_NUMBER%" == "" ? @"Unknown" : "%BUILD_NUMBER%"); >> "$(ProjectDir)\CiInfo.cs"
echo         ///^<summary^>String of the build number and build date/time, and other useful info.^</summary^> >> "$(ProjectDir)\CiInfo.cs"
echo         public const string BuildTag = ("%BUILD_TAG%" == "" ? @"nohudson" : "%BUILD_TAG%") + " built: %DATE%-%TIME%"; >> "$(ProjectDir)\CiInfo.cs"
echo     } >> "$(ProjectDir)\CiInfo.cs"
echo } >> "$(ProjectDir)\CiInfo.cs"

Then I added "CiInfo.cs" to the project, but ignored it from version control. That way I never have to edit it or commit it, and the project always has a constant available that is the latest build number and time.

森林散布 2024-10-14 09:50:59

一种方法是在编译之前添加一个构建步骤,该步骤会在适当的源文件中为 %BUILD_NUMBER% 进行正则表达式替换。

One way to do it is to add a build-step before compilation which does a regex replace in the appropriate source file(s) for %BUILD_NUMBER%.

绿萝 2024-10-14 09:50:59

一种可能性是使用 T4 生成配置类,并实例化所有常量。 T4 很好地集成到 MSVS 中,无需您自己的自定义构建步骤。

One possibility is to use T4 to generate your configuration class with all the constants instantiated. T4 is well-integrated into MSVS, no need for your own custom build step.

左岸枫 2024-10-14 09:50:59

Define 不允许您像在 C/C++ 中那样在 C# 中定义内容。

来自此页面

#define 指令不能用于像 C 和 C++ 中通常所做的那样声明常量值。 C# 中的常量最好定义为类或结构的静态成员。如果您有多个这样的常量,请考虑创建一个单独的“常量”类来保存它们。

如果您希望在 AssemblyInfo 类中反映内部版本号,大多数构建工具都支持在构建时生成该类。 MSBuild 有一个任务NAnt 也是如此。不知道哈德森是如何做到这一点的。

define does not allow you to define contants in C# like you can in C/C++.

From this page:

The #define directive cannot be used to declare constant values as is typically done in C and C++. Constants in C# are best defined as static members of a class or struct. If you have several such constants, consider creating a separate "Constants" class to hold them.

If you are looking to reflect the build number in you AssemblyInfo class, most build tools support generating that class at build time. MSBuild has a task for it. As does NAnt. Not sure how Hudson does this.

尴尬癌患者 2024-10-14 09:50:59

我有类似的问题。

我正在开发一个带有 ASP.Net 后端的 Xamarin 移动应用程序。我有一个包含后端服务器 URL 的设置类:

namespace Company.Mobile
{
    public static class Settings
    {
#if DEBUG
        const string WebApplicationBaseUrl = "https://local-pc:44335/";
#else
        const string WebApplicationBaseUrl = "https://company.com/";
#endif
    }
}

它对于调试和发布配置具有不同的值。但当几个开发人员开始致力于该项目时,这并不起作用。每台开发机器都有自己的 IP 地址,手机需要使用唯一的 IP 地址进行连接。

我需要在每台开发机器上设置文件或环境变量的常量值。这就是 Fody 的用武之地。我用它创建了一个 在解决方案编织器中。以下是详细信息

我将我的 Settings 类放置在 Xamarin 应用项目中。该项目必须包含 Fody Nuget 包:

<ItemGroup>
    <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Fody" Version="6.2.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
    <WeaverFiles Include="$(SolutionDir)Company.Mobile.Models\bin\Debug\netstandard2.0\Company.Mobile.Models.dll" WeaverClassNames="SetDevServerUrlWeaver" />
  </ItemGroup>

我的设置仅在调试配置上工作,因为我不希望在发布版本上发生替换。

Weaver 类放置在移动项目所依赖的类库项目 (Company.Mobile.Models) 中(您不需要也不应该具有此依赖项,但 Fody 文档明确表示包含 Weaver 的项目必须是在发出编织程序集的项目之前构建)。该库项目包括 FodyHelpers Nuget 包:

<ItemGroup Condition="'$(Configuration)' == 'Debug'">
        <PackageReference Include="FodyHelpers" Version="6.2.0" />
    </ItemGroup>

Weaver 类定义如下:

#if DEBUG

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

using Fody;

namespace Company.Mobile.Models
{
    public class SetDevServerUrlWeaver : BaseModuleWeaver
    {
        private const string SettingsClassName = "Settings",
            DevServerUrlFieldName = "WebApplicationBaseUrl",
            DevServerUrlSettingFileName = "devServerUrl.txt";

        public override void Execute()
        {
            var target = this.ModuleDefinition.Types.SingleOrDefault(t => t.IsClass && t.Name == SettingsClassName);

            var targetField = target.Fields.Single(f => f.Name == DevServerUrlFieldName);

            try
            {
                targetField.Constant = File.ReadAllText(Path.Combine(this.ProjectDirectoryPath, DevServerUrlSettingFileName));
            }
            catch
            {
                this.WriteError($"Place a file named {DevServerUrlSettingFileName} and place in it the dev server URL");

                throw;
            }
        }

        public override IEnumerable<string> GetAssembliesForScanning()
        {
            yield return "Company.Mobile";
        }
    }
}

#endif

这是放置在移动应用程序项目中的 FodyWeavers.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <SetDevServerUrlWeaver />
</Weavers>

devServerUrl.txt 仅包含我的本地 IP:
https://192.168.1.111:44335/。不得将此文件添加到源代码管理中。将其添加到源代码管理忽略文件中,以便每个开发人员都有自己的版本。

您可以轻松地从环境变量 (System.Environment.GetEnvironmentVariable) 或任何位置而不是文件中读取替换值。

我希望有更好的方法来做到这一点,比如 Roslyn 或 这个属性似乎可以完成这项工作,但事实并非如此。

I had a similar problem.

I was developing a Xamarin mobile app with an ASP.Net backend. I had a settings class that contains the backend server URL:

namespace Company.Mobile
{
    public static class Settings
    {
#if DEBUG
        const string WebApplicationBaseUrl = "https://local-pc:44335/";
#else
        const string WebApplicationBaseUrl = "https://company.com/";
#endif
    }
}

It has different values for debug and release configurations. But this didn't work when several developers started working on the project. Every dev machine had its IP address, and mobile phones need to connect using unique IP addresses.

I needed to set the constant value from a file or an environment variable on each dev machine. This is where Fody fits in. I used it to create an in solution weaver. Here are the details.

I place my Settings class in the Xamarin app project. This project has to include the Fody Nuget package:

<ItemGroup>
    <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Fody" Version="6.2.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
    <WeaverFiles Include="$(SolutionDir)Company.Mobile.Models\bin\Debug\netstandard2.0\Company.Mobile.Models.dll" WeaverClassNames="SetDevServerUrlWeaver" />
  </ItemGroup>

I make my setup work on Debug configuration only, because I don't want the substitution to happen on Release builds.

The weaver class is placed in a class library project (Company.Mobile.Models) that the mobile project depends on (you needn't and shouldn't have this dependency, but Fody docs says clearly that the project that contains the weaver must be built before the project that emits the weaved assembly). This library project includes the FodyHelpers Nuget package:

<ItemGroup Condition="'$(Configuration)' == 'Debug'">
        <PackageReference Include="FodyHelpers" Version="6.2.0" />
    </ItemGroup>

The weaver class is defined as follows:

#if DEBUG

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

using Fody;

namespace Company.Mobile.Models
{
    public class SetDevServerUrlWeaver : BaseModuleWeaver
    {
        private const string SettingsClassName = "Settings",
            DevServerUrlFieldName = "WebApplicationBaseUrl",
            DevServerUrlSettingFileName = "devServerUrl.txt";

        public override void Execute()
        {
            var target = this.ModuleDefinition.Types.SingleOrDefault(t => t.IsClass && t.Name == SettingsClassName);

            var targetField = target.Fields.Single(f => f.Name == DevServerUrlFieldName);

            try
            {
                targetField.Constant = File.ReadAllText(Path.Combine(this.ProjectDirectoryPath, DevServerUrlSettingFileName));
            }
            catch
            {
                this.WriteError(
quot;Place a file named {DevServerUrlSettingFileName} and place in it the dev server URL");

                throw;
            }
        }

        public override IEnumerable<string> GetAssembliesForScanning()
        {
            yield return "Company.Mobile";
        }
    }
}

#endif

And here's the FodyWeavers.xml file placed in the Mobile app project:

<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <SetDevServerUrlWeaver />
</Weavers>

The devServerUrl.txt simply contains my local IP:
https://192.168.1.111:44335/. This file must not be added to source control. Add it to your source control ignore file so that each developer have his version.

You may easily read the substituted value from an environment variable (System.Environment.GetEnvironmentVariable) or whatever place instead of a file.

I hoped there had been a better way to do this, like Roslyn, or this attribute that seems to do the job, but it doesn't.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文