Visual Studio 2010:在单个文件上运行自定义生成工具时指定工作目录

时间:2014-05-08 23:01:57

标签: c++ visual-studio-2010 visual-studio batch-file visual-studio-2012

我有一个Visual Studio 2010 C ++项目,其中包含需要使用生成C ++代码的内部自定义构建工具处理的文件。

我已经完成了为每个文件添加自定义构建工具的步骤。在自定义生成工具命令行中,使用“call”确保在编译C ++代码之前为所有这些文件运行自定义生成工具。

唯一的问题是自定义构建工具需要从文件所在的目录运行,而不是从Visual Studio解决方案的位置运行。

我现在看到一些可能的选项,但没有一个是理想选择:

  1. 将第一个文件上的自定义构建工具命令行更改为:“cd workingdir&& call custom-tool ...”我不喜欢这个因为它很脆弱 - 如果我添加一个按字母顺序排列的新文件第一个文件,我必须记住它需要在自定义构建步骤中执行此特殊步骤。
  2. 创建一个批处理文件,用于更改工作目录,调用自定义构建工具并进行更改。从Visual Studio调用此批处理文件。
  3. 使用“开始”而不是“调用”来调用自定义构建工具。这带来了为每个文件打开命令shell的缺点(看起来这些命令shell需要手动关闭)。
  4. 是否有更好的选择在工作目录中的文件上运行自定义构建工具?

    谢谢。

2 个答案:

答案 0 :(得分:2)

Visual Studio项目文件是MSBuild文件。因此,您可以在VS中构建项目时手动修改项目文件并执行其他操作。

为此:

  1. 创建一个简单的MSbuild文件,例如MyCustomtool.target。此文件“替代” MSBuild目标并调用您的自定义工具。

这是示例文件:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="BeforeBuild">
    <Message Text="Calling custom tool for %(ClCompile.Identity)"/>
    <Exec Command="customtool.exe %(ClCompile.Identity)" WorkingDirectory="$(ProjectDir)" />
  </Target>
</Project>

此文件: “覆盖”目标名称“ BeforeBuild”,对于在“ ClCompile”组中定义的每个文件,将显示文本消息,并使用名称已编译的.cpp文件作为参数来执行customtool.exe。

  1. 您必须修改.vcxproj文件,并且在最后一行包含</Project>之前,添加文件并添加一行:

<Import Project="MyCustomtool.target"/>

此Studio之后,Visual Studio将为项目中的每个文件执行您的自定义工具。

一些评论:

  • 因为我没有安装VS 2010,所以我不确定100%可以使用哪个目标名称。我认为“ BeforeBuild”对于VS 2010应该是有效的(在VS 2017中,我正在使用“ BeforeBuildGenerateSources”)。

  • 您可以将“工具/选项/项目和解决方案/构建和运行/ MSBuild项目构建输出详细程度”选项更改为“详细”,查看构建过程的详细输出并检查目标名称。

  • 文件组“ ClCompile”包含所有* .cpp文件。您可以在.vcxproj文件中检查文件组名称。
  • 您可以为自定义工具设置工作目录(如上所述)。如果需要在MSBuild文件中,可以使用VisualStudio宏,例如$(ProjectDir),$(OutDir)等。

答案 1 :(得分:0)

您可以创建一个简单的调用程序,以更改工作目录并将构建工具作为子进程调用。您使用Visual Studio,所以我想您的平台是Windows。在这种情况下,应用程序的代码可以如下所示(基于MSDN的示例,它使用C语言编写)

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

int _tmain(int argc, TCHAR *argv[])
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    if (argc < 3)
    {
        printf("Usage: %s working_directory !cmdline\n", argv[0]);
        return 1;
    }

    if (!SetCurrentDirectory(argv[1]))
    {
        printf("SetCurrentDirectory failed (%d)\n", GetLastError());
        return 1;
    }

    LPTSTR commandLine = GetCommandLine();
    while (*commandLine && *commandLine != '!')
        ++commandLine;
    ++commandLine;

    if (!CreateProcess(NULL, commandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
    {
        printf("CreateProcess failed (%d).\n", GetLastError());
        return 1;
    }

    // Wait until child process exits.
    WaitForSingleObject(pi.hProcess, INFINITE);
    DWORD exitCode = 0;
    GetExitCodeProcess(pi.hProcess, &exitCode);

    // Close process and thread handles. 
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return exitCode;
}

它接收新的工作目录作为第一个参数,而不是搜索!在命令行中,并将其后的所有内容都视为子进程命令行。这非常方便,因为您无需打扰引号等。例如

cdandcall.exe C:\Windows !python -c "import os; print(os.path.abspath('.'))"

将在输出中打印C:\Windows。 (当然,如果您将应用程序编译为cdandcall.exe,请在python上放置PATH,并且文件夹C:\Windows存在。)