Visual Studio .NET 2003 の vcproj 生成ツール

何をするのか

Visual Studio .NET 2003 のプロジェクトファイルを生成するためのスクリプト。exe を出力するタイプのプロジェクトと、lib を生成するためのプロジェクトが生成できればよいとする。
いちいち GUI でプロジェクト設定を行うのは面倒なのですよ...。

出力タイプ


その他


プロジェクトについての設定は、スクリプト実行時の引数、および設定ファイルにて指定するものとする。


実装

exe と lib 用のどちらのプロジェクトを生成するかにより、特別に処理すべき内容が異なるので、それぞれの場合毎の実装を行う。このスクリプトが提供するのは、プロジェクトファイルの提供までであって、そのプロジェクト内のファイルを生成したりはしない。


exe ファイル生成用の処理

実現したいこと

くらいか?
この程度であれば、XML の キー指定でタグ置換を行えばよさそう。ただし、Debug, Release の両方への設定が簡単に指定できることが望ましい。


exe ファイル生成用の処理

基本的には, exe と同じ事が実現できればよい。ただし、ライブラリ生成では、そのライブラリの include 用ファイルも生成できると便利なことがある。この生成を、Makefile.am から行えるようにする。同様に、ライブラリのソースコードの情報も Makefile.am から取得できるようにする。つまり、

という感じ。ただし、Makefile.am 内で指定されている .h は必要だが、ソース情報は必要ない場合がある。従って、Makefile.am から何の情報を取り出すかは明確に区別する。 そんな感じ。


実行方法

基本的には、置換すべき情報を格納したファイルを指定して処理を行う。ただし、Makefile.am は `find ...` のようにコマンドラインで渡した方が便利な場合があるので、渡せるようにする。従ってコマンド実行例としては、

  % ruby vcprogen.rb --name followLine --type exe --sources followLine.cpp
  % ruby vcprogen.rb --name vxv --type lib --codes `find . -name "Makefilea.am"`
  % ruby vcprogen.rb vxv_proj.txt --headers `find . -name "Makefile.am"` \
  --sources `find . -name "Makefile.am" ! -path "*/c/*"`
となる。


実行例

vxv2 の Makefile.am から lib 生成用の vxv2.pcproj を作る例は以下の通り。

  % ruby vcprogen.rb --name vxv2 --type lib --mask "cpp,h" \
  --headers "`find ${VXV2_PATH} -name "Makefile.am"`" \
  --sources "`find ${VXV2_PATH} -name "Makefile.am" \
  ! -path "*/monitor/*" ! -path "*/guiCtrl/*" ! -path "*/example/*" \
  ! -path "*/tools/*"`" vxv2_cfg.txt 


vxv2_cfg.txt の中身

/Configuration/Tool/RuntimeTypeInfo     TRUE
/Configuration/Tool/RuntimeLibrary      2
true    /Configuration/Tool/PreprocessorDefinitions     ;HAVE_CONFIG_H


followLine.exe 用の followLine.vcproj を作る例は以下の通り。

  % ruby ${VCPROGEN} --name followLine --type exe --sources "followLine.cpp" 


スクリプトのソースコード

こんな感じ

#!/usr/bin/env ruby
#
# Visual Studio .NET 2003 の vcproj 生成ツール
# Satofumi KAMIMURA
# $Id$

# 置換処理
def replace(template, key, value, add = false)
  
  # 検索パターンの生成
  path = key.split('/') - [""]
  name = path.pop
  parent = path.pop

  reg_str = '(<'
  path.each { |tag|
    reg_str += tag + '.+?<'
  }
  reg_str += parent + '.+?' + name + '=")(.+?)(")'

  left = template
  template = ''
  pattern = Regexp.compile(reg_str, Regexp::MULTILINE)
  while left =~ pattern
    template += $` + $1 + ((add) ? $2 : '') + value + $3
    left = $'
  end
  
  return template + left
end


# ファイルの行毎に置換処理
def replaceFromFile(template, config_file)
  open(config_file) { |io|
    while line = io.gets
      tags = line.chomp.split(/\s/, 2)
      if tags.size() >= 2
        if tags[0].downcase == "true"
          add_tags = tags[1].split(/\s/, 2)
          template = replace(template, add_tags[0], add_tags[1], true)
        else
          template = replace(template, tags[0], tags[1])
        end
      end
    end
    return template
  } rescue STDERR.puts "replaceFromFile: Warning: #$!"
end

# Makefile の指定タグに指定されているファイルを展開


# include パスを追加
def addIncludes(includes, file)
  includes.push(File.dirname(file))
end


# 1行分の行の取り出し
def parseLine(line, io, dir_path)
  while line[-1,1] == '\\'
    line = line.chop + io.gets
  end
  files = line.split
  files.map! { |file| dir_path + '/' + file }
  return files.join(' ')
end


# Makefile.am を展開する
def extendMakefile(file, keyword = 'SOURCES')
  pattern = Regexp.compile('^\w+?_' + keyword + '\s+=')
  open(file) { |io|
    while line = io.gets
      line.strip!
      if pattern =~ line
        return parseLine($', io, File.dirname(file))
      end
    end
  } rescue STDERR.puts "extendMakefile: Warning: #$!"
  return ""
end


# コードに含めるファイル一覧を生成
def createFileList(sources, headers, includes, files, mask)
  files.split(/\s/).each { |file|
    if File.basename(file) == "Makefile.am"
      createFileList(sources, headers, includes, extendMakefile(file), mask)
      next
    end
    ext = File.extname(file)[1..-1]
    if !mask.split(/[,\s]/).index(ext)
      next
    end

    addIncludes(includes, file)
    if ext == "h" || ext == "hh"
      headers.push(file)
    else
      sources.push(file)
    end
  }
end


# ファイルタグの生成
def createFilesXML(list, offset)
  tags = ''
  list.each { |file|
    tags +=
    offset + '<File RelativePath="' + file.gsub('/', '\\') + "\"></File>\n"
  }
  return tags
end


# ソースファイルを挿入
def insertSources(template, sources)
  
  tags = createFilesXML(sources, "\t\t\t")
  if tags.empty?
    return
  end
  pattern = /(<Filter\s+Name="Source files".+?\}">)(.+?<\/Filter>)/m
  if template =~ pattern
    template.sub!(pattern, $1 + "\n" + tags + $2)
  end
end


# ヘッダファイルを挿入
def insertHeaders(template, headers)

  tags = createFilesXML(headers, "\t\t\t")
  if tags.empty?
    return
  end
  pattern = /<Filter\s+Name="Header files".+?\}">^.+?<\/Filter>/m
  if template =~ pattern
    template.sub!(pattern, $1 + "\n" + tags + $2)
  end
end


# include/ の生成
def createIncludes(template, includes, files)
  targets = []
  files.split(/\s/).each { |file|
    if File.basename(file) == "Makefile.am"
      targets += extendMakefile(file, "HEADERS").split(' ')
    else
      includes << file
    end
  }

  offset = "\t\t\t\t"
  cmd = 'del /q include; ' + "\n" + offset + 'mkdir include; ' + "\n"
  targets.each { |file|
    cmd += offset + 'copy ' + file.gsub('/', '\\') + ' include\; ' + "\n"
  }
  return replace(template, '/Configuration/Tool/CommandLine', cmd.chomp)
end


# 追加のインクルードパスを挿入
def insertIncludes(template, includes)

  replace = '.\; .\include; ' + includes.uniq.join('; ').gsub('/', '\\') + ';'
  path = "/Configuration/Tool/AdditionalIncludeDirectories"
  return replace(template, path, replace)
end


# lib 生成用テンプレート
lib_template = <<-EOB
<?xml version="1.0" encoding="shift_jis"?>
<VisualStudioProject
        ProjectType="Visual C++"
        Version="7.10"
        Name="lib_template"
        ProjectGUID="{E35DE899-8736-4A8E-A604-821469165AF1}"
        RootNamespace="lib_template"
        Keyword="ManagedCProj">
        <Platforms>
                <Platform
                        Name="Win32"/>
        </Platforms>
        <Configurations>
                <Configuration
                        Name="Debug|Win32"
                        OutputDirectory="$(SolutionDir)$(ConfigurationName)"
                        IntermediateDirectory="$(ConfigurationName)"
                        ConfigurationType="4"
                        CharacterSet="2"
                        ManagedExtensions="FALSE">
                        <Tool
                                Name="VCCLCompilerTool"
                                Optimization="0"
                                AdditionalIncludeDirectories=";"
                                PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
                                MinimalRebuild="TRUE"
                                BasicRuntimeChecks="0"
                                RuntimeLibrary="1"
                                RuntimeTypeInfo="FALSE"
                                WarningLevel="3"
                                DebugInformationFormat="4"/>
                        <Tool
                                Name="VCCustomBuildTool"/>
                        <Tool
                                Name="VCLibrarianTool"/>
                        <Tool
                                Name="VCMIDLTool"/>
                        <Tool
                                Name="VCPostBuildEventTool"/>
                        <Tool
                                Name="VCPreBuildEventTool"
                                CommandLine=";"/>
                        <Tool
                                Name="VCPreLinkEventTool"/>
                        <Tool
                                Name="VCResourceCompilerTool"/>
                        <Tool
                                Name="VCWebServiceProxyGeneratorTool"/>
                        <Tool
                                Name="VCXMLDataGeneratorTool"/>
                        <Tool
                                Name="VCManagedWrapperGeneratorTool"/>
                        <Tool
                                Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
                </Configuration>
                <Configuration
                        Name="Release|Win32"
                        OutputDirectory="$(SolutionDir)$(ConfigurationName)"
                        IntermediateDirectory="$(ConfigurationName)"
                        ConfigurationType="1"
                        CharacterSet="2"
                        ManagedExtensions="FALSE">
                        <Tool
                                Name="VCCLCompilerTool"
                                AdditionalIncludeDirectories=";"
                                PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
                                MinimalRebuild="TRUE"
                                RuntimeLibrary="0"
                                RuntimeTypeInfo="FALSE"
                                WarningLevel="3"
                                DebugInformationFormat="4"/>
                        <Tool
                                Name="VCCustomBuildTool"/>
                        <Tool
                                Name="VCLinkerTool"
                                OutputFile="$(OutDir)\\$(ProjectName).exe"
                                LinkIncremental="1"
                                GenerateDebugInformation="TRUE"/>
                        <Tool
                                Name="VCMIDLTool"/>
                        <Tool
                                Name="VCPostBuildEventTool"/>
                        <Tool
                                Name="VCPreBuildEventTool"
                                CommandLine=";"/>
                        <Tool
                                Name="VCPreLinkEventTool"/>
                        <Tool
                                Name="VCResourceCompilerTool"/>
                        <Tool
                                Name="VCWebServiceProxyGeneratorTool"/>
                        <Tool
                                Name="VCXMLDataGeneratorTool"/>
                        <Tool
                                Name="VCWebDeploymentTool"/>
                        <Tool
                                Name="VCManagedWrapperGeneratorTool"/>
                        <Tool
                                Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
                </Configuration>
        </Configurations>
        <References>
        </References>
        <Files>
                <Filter
                        Name="Source files"
                        Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
                        UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
                </Filter>
                <Filter
                        Name="Header files"
                        Filter="h;hpp;hxx;hm;inl;inc;xsd"
                        UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
                </Filter>
                <Filter
                        Name="Resource files"
                        Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
                        UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
                </Filter>
        </Files>
        <Globals>
        </Globals>
</VisualStudioProject>
EOB


# exe 生成用テンプレート
exe_template = <<-EOB
<?xml version="1.0" encoding="shift_jis"?>
<VisualStudioProject
        ProjectType="Visual C++"
        Version="7.10"
        Name="exe_template"
        ProjectGUID="{BA46C739-97CD-4F68-9F3D-ECF9759134CE}"
        RootNamespace="exe_template"
        Keyword="ManagedCProj">
        <Platforms>
                <Platform
                        Name="Win32"/>
        </Platforms>
        <Configurations>
                <Configuration
                        Name="Debug|Win32"
                        OutputDirectory="$(SolutionDir)$(ConfigurationName)"
                        IntermediateDirectory="$(ConfigurationName)"
                        ConfigurationType="1"
                        CharacterSet="2"
                        ManagedExtensions="FALSE">
                        <Tool
                                Name="VCCLCompilerTool"
                                Optimization="0"
                                AdditionalIncludeDirectories=";"
                                PreprocessorDefinitions="WIN32;_DEBUG"
                                MinimalRebuild="TRUE"
                                BasicRuntimeChecks="0"
                                RuntimeLibrary="1"
                                RuntimeTypeInfo="FALSE"
                                WarningLevel="3"
                                DebugInformationFormat="4"/>
                        <Tool
                                Name="VCCustomBuildTool"/>
                        <Tool
                                Name="VCLinkerTool"
                                OutputFile="$(OutDir)\\$(ProjectName).exe"
                                LinkIncremental="2"
                                SuppressStartupBanner="TRUE"
                                GenerateDebugInformation="TRUE"
                                AssemblyDebug="1"/>
                        <Tool
                                Name="VCMIDLTool"/>
                        <Tool
                                Name="VCPostBuildEventTool"/>
                        <Tool
                                Name="VCPreBuildEventTool"
                                CommandLine=";"/>
                        <Tool
                                Name="VCPreLinkEventTool"/>
                        <Tool
                                Name="VCResourceCompilerTool"/>
                        <Tool
                                Name="VCWebServiceProxyGeneratorTool"/>
                        <Tool
                                Name="VCXMLDataGeneratorTool"/>
                        <Tool
                                Name="VCWebDeploymentTool"/>
                        <Tool
                                Name="VCManagedWrapperGeneratorTool"/>
                        <Tool
                                Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
                </Configuration>
                <Configuration
                        Name="Release|Win32"
                        OutputDirectory="$(SolutionDir)$(ConfigurationName)"
                        IntermediateDirectory="$(ConfigurationName)"
                        ConfigurationType="1"
                        CharacterSet="2"
                        ManagedExtensions="FALSE">
                        <Tool
                                Name="VCCLCompilerTool"
                                AdditionalIncludeDirectories=";"
                                PreprocessorDefinitions="WIN32;NDEBUG"
                                MinimalRebuild="TRUE"
                                RuntimeLibrary="0"
                                RuntimeTypeInfo="FALSE"
                                WarningLevel="3"
                                DebugInformationFormat="4"/>
                        <Tool
                                Name="VCCustomBuildTool"/>
                        <Tool
                                Name="VCLinkerTool"
                                OutputFile="$(OutDir)\\$(ProjectName).exe"
                                LinkIncremental="1"
                                GenerateDebugInformation="TRUE"/>
                        <Tool
                                Name="VCMIDLTool"/>
                        <Tool
                                Name="VCPostBuildEventTool"/>
                        <Tool
                                Name="VCPreBuildEventTool"
                                CommandLine=";"/>
                        <Tool
                                Name="VCPreLinkEventTool"/>
                        <Tool
                                Name="VCResourceCompilerTool"/>
                        <Tool
                                Name="VCWebServiceProxyGeneratorTool"/>
                        <Tool
                                Name="VCXMLDataGeneratorTool"/>
                        <Tool
                                Name="VCWebDeploymentTool"/>
                        <Tool
                                Name="VCManagedWrapperGeneratorTool"/>
                        <Tool
                                Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
                </Configuration>
        </Configurations>
        <References>
        </References>
        <Files>
                <Filter
                        Name="Source files"
                        Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
                        UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
                </Filter>
                <Filter
                        Name="Header files"
                        Filter="h;hpp;hxx;hm;inl;inc;xsd"
                        UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
                </Filter>
                <Filter
                        Name="Resource files"
                        Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
                        UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
                </Filter>
        </Files>
        <Globals>
        </Globals>
</VisualStudioProject>
EOB


# 使い方を表示して終了
def printUsage()
  print \
  "usage:\n" \
  "\t% ruby " + __FILE__ + " [options] <config.txt>\n" \
  "\n" \
  "options: \n" \
  "--name <project name>           specify project name\n" \
  "--type <exe, lib>               specify project output type\n" \
  "--mask \"cpp,c,h\"                specify handled file type\n" \
  "--sources \"<Makefile.am list>\"  to create sources list\n" \
  "--headers \"<Makefile.am list>\"  to create include folder\n" \
  "--codes   \"<Makefile.am list>\"  means sourcesb and headers option\n" \
  "\n"
  exit
end


# メイン処理
if ARGV.empty?
  printUsage()
end

# オプションの判定
pre_arg = nil
proj_name = nil
proj_type = nil
proj_mask = "c,cpp,h"
proj_sources = nil
proj_headers = nil
files = []
ARGV.reverse.each { |arg|
  case arg
  when /--name/
    proj_name = pre_arg.split('.')[0]
    files.shift

  when /--type/
    if pre_arg == "exe" || pre_arg == "lib"
      proj_type = pre_arg
    end
    files.shift

  when /--mask/
    proj_mask = pre_arg
    files.shift

  when /--sources/
    proj_sources = pre_arg
    files.shift

  when /--headers/
    proj_headers = pre_arg
    files.shift

  when /--codes/
    proj_sources = pre_arg
    proj_headers = pre_arg
    files.shift

  else
    files.unshift(arg)
  end
  pre_arg = arg
}
config_file = files.shift
if proj_name == nil
  print "name is not specified!\n"
  printUsage()
end
if proj_type == nil
  print "type is not specified!\n"
  printUsage()
end
template = (proj_type == "exe") ? exe_template : lib_template

# プロジェクト名の置換
template = replace(template, "/VisualStudioProject/Name", proj_name)
template = replace(template, "/VisualStudioProject/RootNamespace", proj_name)

# コンフィグファイル指定の要素を置換
if config_file
  template = replaceFromFile(template, config_file)
end

sources = []
headers = []
includes = []

# source files, include path の調整
if proj_sources
  createFileList(sources, headers, includes, proj_sources, proj_mask)
end

# include folder の生成
if proj_headers
  template = createIncludes(template, includes, proj_headers)
end

# sources, headers, includes の反映
insertSources(template, sources)
insertHeaders(template, headers)
template = insertIncludes(template, includes)


# プロジェクトファイルの生成
open(proj_name + '.vcproj', 'w') { |io|
  io.print template
}

以上


Generated on Mon Apr 13 22:52:06 2009 by  doxygen 1.5.7.1