Проблема: у нас есть процесс, который создает наш исполняемый файл. Перед запуском сборки проекта установки/развертывания я создаю файлы, которые необходимо включить в папку FileSystem/Common Documents проекта установки/развертывания. В случаях, когда имя файла всегда создается как одно и то же имя файла, это довольно просто; однако у меня есть случай, когда я не могу заранее определить имя файла, поскольку процесс может и будет создавать уникальное имя файла при каждом последующем запуске.
Вопрос: как программно добавлять файлы в папку FileSystem/Common Documents?
Мои исследования. Я изучил пользовательские действия, но не знаю, как сослаться на файловую систему проекта установки/развертывания, чтобы добавить эти файлы.
Дополнительная информация В рамках нашего ежедневного процесса сборки мы создаем http://lucene.apache.org/(Lucene) индексы, в которых файлы в формате *.cfs могут иметь другие имена файлов, чем в предыдущий день. Поскольку мы не хотим открывать файл vdproj в Visual Studio и заменять имя файла вручную с помощью редактора файловой системы, нам нужен более автоматизированный подход.
Наше решение В качестве решения я использовал http://www.ewaldhofman.nl/post/2011/04/06/Customize-Team-Build-2010-e28093-Part-16-Specify-the-relative-reference-path.aspx (Эвальд Хофман) — отличное руководство по TFS Team Build. В этом я воспроизвел его действия по извлечению и возврату, добавив при этом свое собственное пользовательское действие, которое открывает файл vdproj и редактирует файл в соответствии с предварительно сгенерированными именами файлов индекса Lucene.
Пример кода
protected override void Execute(CodeActivityContext context)
{
string fileFullName = context.GetValue(this.FileFullName);
string dropFolder = context.GetValue(this.DropLocation);
string[] indexerNames = context.GetValue(this.LuceneIndexes);
try
{
//read the vdproj file into memory
string text = File.ReadAllText(fileFullName);
//for each lucene index folder
foreach (string index in indexerNames)
{
//traversing twice so that the lucene index and spell index can be handled
//these are subfolder names we use to segregate our normal lucene index from our spelling indexes.
foreach (string subFolderInLuceneIndex in new string[] { "en_US_9", "en_US_9_sp" })
{
//retrieve all the files in folder \\[DropFolder]\[index]\[subFolderInLuceneIndex]\*.cfs
foreach (string file in Directory.GetFiles(System.IO.Path.Combine(dropFolder, index, subFolderInLuceneIndex), "*.cfs"))
{
FileInfo cfsFile = new FileInfo(file);
context.TrackBuildMessage(string.Format("Exiting file in lucene index directory: {0}", cfsFile.FullName));
string fileNamePattern = ".+.cfs";
string div = Dividor(4);
//matching pattern for sourcepath ie("SourcePath" = "8:\\\\...\\[index]\\[subFolderInLuceneIndex]\\_0.cfs")
string sourcePattern = string.Format("(\".+{1}{0}{2}{0}{3})", div, index, subFolderInLuceneIndex, fileNamePattern);
//matching pattern for targetname ie("TargetName" = "8:_0.cfs")
string targetPattern = string.Format("(\"TargetName\"\\s=\\s\"8:{0})", fileNamePattern);
StringBuilder sb = new StringBuilder();
sb.Append(sourcePattern);
sb.Append("(.+\\r\\n.+)"); //carriage return between targetpattern and sourcepattern
sb.AppendFormat(targetPattern);
//(.+[index]\\\\[subFolderInLuceneIndex].+.cfs)(.+\r\n.+)(TargetName.+8:.+.cfs)
MatchCollection matches = Regex.Matches(text, sb.ToString(), RegexOptions.Multiline);
//if more than one match exists, a problem with the setup and deployment file exists
if (matches.Count != 1)
{
throw new Exception("There should exist one and only one match.");
}
else
{
foreach (Match match in matches)
{
string newText = text;
string existingPattern = match.Value;
if (match.Groups != null)
{
//if the value found using the match doesn't contain the filename, insert the filename
//into the text
if (!match.Value.Contains(cfsFile.Name))
{
//matched by sourcePattern
string sourceValue = match.Groups[1].Value;
//matched by targetPattern
string targetNameValue = match.Groups[3].Value;
int idIndex = targetNameValue.IndexOf("8:") + 2;
//get the old *.cfs file name
string oldFileName = targetNameValue.Substring(idIndex, targetNameValue.Length - idIndex);
//replace old cfs file name with new cfs file name in the target pattern
string newTargetNameValue = Regex.Replace(targetNameValue, oldFileName, cfsFile.Name);
//replace old cfs file name with new cfs file name in the source pattern
string newSourceValue = Regex.Replace(sourceValue, oldFileName, cfsFile.Name);
//construct the new text that will be written to the file
StringBuilder newSb = new StringBuilder();
newSb.Append(newSourceValue);
//account for the quote, carriage return and tabs. this ensures we maintain proper
//formatting for a vdproj file
newSb.Append("\"\r\n\t\t\t");
newSb.AppendFormat(newTargetNameValue);
newText = Regex.Replace(text, sb.ToString(), newSb.ToString(), RegexOptions.Multiline);
File.WriteAllText(fileFullName, newText);
context.TrackBuildMessage(string.Format("Text {0} replaced with {1}.", oldFileName, cfsFile.Name));
}
else
{
context.TrackBuildMessage("No change applied for current file.");
}
}
}
}
}
}
}
}
catch (Exception ex)
{
context.TrackBuildError(ex.ToString());
throw ex;
}
}
private static string Dividor(int n)
{
return new String('\\', n);
}