2020-08-21 02:18:14 -06:00
|
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
<!--
|
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2018 The Qt Company Ltd.
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
**
|
|
|
|
** This file is part of the Qt VS Tools.
|
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
**
|
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
-->
|
|
|
|
|
|
|
|
<!--
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Helper inline tasks used by the Qt/MSBuild targets
|
|
|
|
// -->
|
2022-04-18 02:05:21 -06:00
|
|
|
<Project>
|
2020-08-21 02:18:14 -06:00
|
|
|
|
|
|
|
<!--
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// TASK ListQrc
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// List resource paths in a QRC file.
|
|
|
|
// Parameters:
|
|
|
|
// in string QrcFilePath: path to QRC file
|
|
|
|
// out string[] Result: paths to files referenced in QRC
|
|
|
|
// -->
|
|
|
|
<UsingTask TaskName="ListQrc"
|
|
|
|
TaskFactory="CodeTaskFactory"
|
|
|
|
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<QrcFilePath ParameterType="System.String" Required="true" />
|
|
|
|
<Result ParameterType="System.String[]" Output="true" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Reference Include="System.Xml"/>
|
|
|
|
<Reference Include="System.Xml.Linq"/>
|
|
|
|
<Using Namespace="System.Xml.Linq"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
XDocument qrc = XDocument.Load(QrcFilePath, LoadOptions.SetLineInfo);
|
|
|
|
IEnumerable<XElement> files = qrc
|
|
|
|
.Element("RCC")
|
|
|
|
.Elements("qresource")
|
|
|
|
.Elements("file");
|
|
|
|
Uri QrcPath = new Uri(QrcFilePath);
|
|
|
|
Result = files
|
|
|
|
.Select(x => new Uri(QrcPath, x.Value).LocalPath)
|
|
|
|
.ToArray();
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// TASK GetItemHash
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Calculate an hash code (Deflate + Base64) for an item, given a list of metadata to use as key
|
|
|
|
// Parameters:
|
|
|
|
// in ITaskItem Item: item for which the hash will be calculated
|
|
|
|
// in string[] Keys: list of names of the metadata to use as item key
|
|
|
|
// out string Hash: hash code (Base64 representation of Deflate'd UTF-8 item key)
|
|
|
|
// -->
|
|
|
|
<UsingTask TaskName="GetItemHash"
|
|
|
|
TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<Item ParameterType="Microsoft.Build.Framework.ITaskItem" Required="true" />
|
|
|
|
<Keys ParameterType="System.String[]" Required="true" />
|
|
|
|
<Hash Output="true" ParameterType="System.String" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Using Namespace="System"/>
|
|
|
|
<Using Namespace="System.Text"/>
|
|
|
|
<Using Namespace="System.IO"/>
|
|
|
|
<Using Namespace="System.IO.Compression"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
var data = Encoding.UTF8.GetBytes(string.Concat(Keys.OrderBy(x => x)
|
|
|
|
.Select(x => string.Format("[{0}={1}]", x, Item.GetMetadata(x))))
|
|
|
|
.ToUpper());
|
|
|
|
using (var dataZipped = new MemoryStream()) {
|
|
|
|
using (var zip = new DeflateStream(dataZipped, CompressionLevel.Fastest))
|
|
|
|
zip.Write(data, 0, data.Length);
|
|
|
|
Hash = Convert.ToBase64String(dataZipped.ToArray());
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// TASK Expand
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Expand a list of items, taking additional metadata from a base item and from a template item.
|
|
|
|
// Parameters:
|
|
|
|
// in ITaskItem[] Items: items to expand
|
|
|
|
// in ITaskItem BaseItem: base item from which the expanded items derive
|
|
|
|
// in ITaskItem Template = null: (optional) template containing metadata to add / update
|
|
|
|
// out ITaskItem[] Result: list of new items
|
|
|
|
// -->
|
|
|
|
<UsingTask TaskName="Expand"
|
|
|
|
TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<Items ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
|
|
|
|
<BaseItem ParameterType="Microsoft.Build.Framework.ITaskItem" Required="true" />
|
|
|
|
<Template ParameterType="Microsoft.Build.Framework.ITaskItem" Required="false"/>
|
|
|
|
<Result Output="true" ParameterType="Microsoft.Build.Framework.ITaskItem[]" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Using Namespace="System"/>
|
|
|
|
<Using Namespace="System.Text.RegularExpressions"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
Result = new ITaskItem[] { };
|
|
|
|
var reserved = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
|
|
|
|
{
|
|
|
|
"AccessedTime", "CreatedTime", "DefiningProjectDirectory",
|
|
|
|
"DefiningProjectExtension", "DefiningProjectFullPath", "DefiningProjectName",
|
|
|
|
"Directory", "Extension", "Filename", "FullPath", "Identity", "ModifiedTime",
|
|
|
|
"RecursiveDir", "RelativeDir", "RootDir",
|
|
|
|
};
|
|
|
|
var newItems = new List<ITaskItem>();
|
|
|
|
foreach (var item in Items) {
|
|
|
|
var newItem = new TaskItem(item);
|
|
|
|
if (BaseItem != null)
|
|
|
|
BaseItem.CopyMetadataTo(newItem);
|
|
|
|
var itemExt = newItem.GetMetadata("Extension");
|
|
|
|
if (!string.IsNullOrEmpty(itemExt))
|
|
|
|
newItem.SetMetadata("Suffix", itemExt.Substring(1));
|
|
|
|
if (Template != null) {
|
|
|
|
var metadataNames = Template.MetadataNames
|
|
|
|
.Cast<string>().Where(x => !reserved.Contains(x));
|
|
|
|
foreach (var metadataName in metadataNames) {
|
|
|
|
var metadataValue = Template.GetMetadata(metadataName);
|
|
|
|
newItem.SetMetadata(metadataName,
|
|
|
|
Regex.Replace(metadataValue, @"(%<)(\w+)(>)",
|
|
|
|
match => newItem.GetMetadata(match.Groups[2].Value)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
newItems.Add(newItem);
|
|
|
|
}
|
|
|
|
Result = newItems.ToArray();
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// TASK DumpItems
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Dump contents of items as a log message. The contents are formatted as XML.
|
|
|
|
// Parameters:
|
|
|
|
// in string ItemType: type of the items; this is used as the parent node of each
|
|
|
|
// item dump
|
|
|
|
// in ITaskItem[] Items: items to dump
|
|
|
|
// in bool DumpReserved: include MSBuild reserved metadata in dump?
|
|
|
|
// in string Metadata: list of names of metadata to include in dump; omit to
|
|
|
|
// include all metadata
|
|
|
|
// -->
|
|
|
|
<UsingTask TaskName="DumpItems"
|
|
|
|
TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<ItemType ParameterType="System.String" Required="true" />
|
|
|
|
<Items ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
|
|
|
|
<DumpReserved ParameterType="System.Boolean" Required="false" />
|
|
|
|
<Metadata ParameterType="System.String" Required="false" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Using Namespace="System"/>
|
|
|
|
<Using Namespace="System.Diagnostics"/>
|
|
|
|
<Using Namespace="System.Text"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
var reserved = new HashSet<string>
|
|
|
|
{
|
|
|
|
"AccessedTime", "CreatedTime", "DefiningProjectDirectory",
|
|
|
|
"DefiningProjectExtension", "DefiningProjectFullPath", "DefiningProjectName",
|
|
|
|
"Directory", "Extension", "Filename", "FullPath", "Identity", "ModifiedTime",
|
|
|
|
"RecursiveDir", "RelativeDir", "RootDir",
|
|
|
|
};
|
|
|
|
if (Metadata == null)
|
|
|
|
Metadata = "";
|
|
|
|
var requestedNames = new HashSet<string>(Metadata.Split(new[] { ';' },
|
|
|
|
StringSplitOptions.RemoveEmptyEntries));
|
|
|
|
var itemXml = new StringBuilder();
|
|
|
|
if (Items.Any()) {
|
|
|
|
foreach (var item in Items) {
|
|
|
|
if (itemXml.Length > 0)
|
|
|
|
itemXml.Append("\r\n");
|
|
|
|
itemXml.AppendFormat("<{0} Include=\"{1}\"", ItemType, item.ItemSpec);
|
|
|
|
var names = item.MetadataNames.Cast<string>()
|
|
|
|
.Where(x => (DumpReserved || !reserved.Contains(x))
|
|
|
|
&& (!requestedNames.Any() || requestedNames.Contains(x)))
|
|
|
|
.OrderBy(x => x);
|
|
|
|
if (names.Any()) {
|
|
|
|
itemXml.Append(">\r\n");
|
|
|
|
foreach (string name in names) {
|
|
|
|
if (!DumpReserved && reserved.Contains(name))
|
|
|
|
continue;
|
|
|
|
if (!item.MetadataNames.Cast<string>().Contains(name))
|
|
|
|
continue;
|
|
|
|
var value = item.GetMetadata(name);
|
|
|
|
if (!string.IsNullOrEmpty(value))
|
|
|
|
itemXml.AppendFormat(" <{0}>{1}</{0}>\r\n", name, value);
|
|
|
|
else
|
|
|
|
itemXml.AppendFormat(" <{0}/>\r\n", name);
|
|
|
|
}
|
|
|
|
itemXml.AppendFormat("</{0}>", ItemType);
|
|
|
|
} else {
|
|
|
|
itemXml.Append("/>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
itemXml.AppendFormat("<{0}/>", ItemType);
|
|
|
|
}
|
|
|
|
Log.LogMessage(MessageImportance.High, itemXml.ToString());
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// TASK QtRunWork
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Run work items in parallel processes.
|
|
|
|
// Parameters:
|
|
|
|
// in ITaskItem[] QtWork: work items
|
|
|
|
// in int QtMaxProcs: maximum number of processes to run in parallel
|
|
|
|
// in bool QtDebug: generate debug messages
|
|
|
|
// out ITaskItem[] Result: list of new items with the result of each work item
|
|
|
|
// -->
|
|
|
|
<UsingTask
|
|
|
|
TaskName="QtRunWork"
|
|
|
|
TaskFactory="CodeTaskFactory"
|
|
|
|
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<QtWork ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
|
|
|
|
<QtMaxProcs ParameterType="System.Int32" Required="true" />
|
|
|
|
<QtDebug ParameterType="System.Boolean" Required="true" />
|
|
|
|
<Result Output="true" ParameterType="Microsoft.Build.Framework.ITaskItem[]" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Using Namespace="System"/>
|
|
|
|
<Using Namespace="System.IO"/>
|
|
|
|
<Using Namespace="System.Diagnostics"/>
|
|
|
|
<Using Namespace="System.Collections.Generic"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
Result = new ITaskItem[] { };
|
|
|
|
bool ok = true;
|
|
|
|
var Comparer = StringComparer.InvariantCultureIgnoreCase;
|
|
|
|
var Comparison = StringComparison.InvariantCultureIgnoreCase;
|
|
|
|
|
|
|
|
// Work item key = "%(WorkType)(%(Identity))"
|
|
|
|
Func<string, string, string> KeyString = (x, y) => string.Format("{0}{{{1}}}", x, y);
|
|
|
|
Func<ITaskItem, string> Key = (item) =>
|
|
|
|
KeyString(item.GetMetadata("WorkType"), item.ItemSpec);
|
|
|
|
var workItemKeys = new HashSet<string>(QtWork.Select(x => Key(x)), Comparer);
|
|
|
|
|
|
|
|
// Work items, indexed by %(Identity)
|
|
|
|
var workItemsByIdentity = QtWork
|
|
|
|
.GroupBy(x => x.ItemSpec, x => Key(x), Comparer)
|
|
|
|
.ToDictionary(x => x.Key, x => new List<string>(x), Comparer);
|
|
|
|
|
|
|
|
// Work items, indexed by work item key
|
|
|
|
var workItems = QtWork.Select(x => new
|
|
|
|
{
|
|
|
|
Self = x,
|
|
|
|
Key = Key(x),
|
|
|
|
ToolPath = x.GetMetadata("ToolPath"),
|
|
|
|
Message = x.GetMetadata("Message"),
|
|
|
|
DependsOn = new HashSet<string>(comparer: Comparer,
|
|
|
|
collection: x.GetMetadata("DependsOn")
|
|
|
|
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
|
|
|
.Where(y => workItemsByIdentity.ContainsKey(y))
|
|
|
|
.SelectMany(y => workItemsByIdentity[y])
|
|
|
|
.Union(x.GetMetadata("DependsOnWork")
|
|
|
|
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
|
|
|
.Select(y => KeyString(y, x.ItemSpec))
|
|
|
|
.Where(y => workItemKeys.Contains(y)))
|
|
|
|
.GroupBy(y => y, Comparer).Select(y => y.Key)
|
|
|
|
.Where(y => !y.Equals(Key(x), Comparison))),
|
|
|
|
ProcessStartInfo = new ProcessStartInfo
|
|
|
|
{
|
|
|
|
FileName = x.GetMetadata("ToolPath"),
|
|
|
|
Arguments = x.GetMetadata("Options"),
|
|
|
|
CreateNoWindow = true,
|
|
|
|
UseShellExecute = false,
|
|
|
|
RedirectStandardError = true,
|
|
|
|
RedirectStandardOutput = true,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
// In case of items with duplicate keys, use only the first one
|
|
|
|
.GroupBy(x => x.Key, Comparer)
|
|
|
|
.ToDictionary(x => x.Key, x => x.First(), Comparer);
|
|
|
|
|
|
|
|
// Result
|
|
|
|
var result = workItems.Values
|
|
|
|
.ToDictionary(x => x.Key, x => new TaskItem(x.Self));
|
|
|
|
|
|
|
|
// Dependency relation [item -> dependent items]
|
|
|
|
var dependentsOf = workItems.Values
|
|
|
|
.Where(x => x.DependsOn.Any())
|
|
|
|
.SelectMany(x => x.DependsOn.Select(y => new { Dependent = x.Key, Dependency = y }))
|
|
|
|
.GroupBy(x => x.Dependency, x => x.Dependent, Comparer)
|
|
|
|
.ToDictionary(x => x.Key, x => new List<string>(x), Comparer);
|
|
|
|
|
|
|
|
// Work items that are ready to start; initially queue all independent items
|
|
|
|
var workQueue = new Queue<string>(workItems.Values
|
|
|
|
.Where(x => !x.DependsOn.Any())
|
|
|
|
.Select(x => x.Key));
|
|
|
|
|
|
|
|
if (QtDebug) {
|
|
|
|
Log.LogMessage(MessageImportance.High,
|
|
|
|
string.Format("## QtRunWork queueing\r\n## {0}",
|
|
|
|
string.Join("\r\n## ", workQueue)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Postponed items; save dependent items to queue later when ready
|
|
|
|
var postponedItems = new HashSet<string>(workItems.Values
|
|
|
|
.Where(x => x.DependsOn.Any())
|
|
|
|
.Select(x => x.Key));
|
|
|
|
|
|
|
|
if (QtDebug && postponedItems.Any()) {
|
|
|
|
Log.LogMessage(MessageImportance.High,
|
|
|
|
string.Format("## QtRunWork postponed dependents\r\n## {0}",
|
|
|
|
string.Join("\r\n## ", postponedItems
|
|
|
|
.Select(x => string.Format("{0} <- {1}", x,
|
|
|
|
string.Join(", ", workItems[x].DependsOn))))));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Work items that are running; must synchronize with the exit of all processes
|
|
|
|
var running = new Queue<KeyValuePair<string, Process>>();
|
|
|
|
|
|
|
|
// Work items that have terminated
|
|
|
|
var terminated = new HashSet<string>(Comparer);
|
|
|
|
|
|
|
|
// While there are work items queued, start a process for each item
|
|
|
|
while (ok && workQueue.Any()) {
|
|
|
|
|
|
|
|
var workItem = workItems[workQueue.Dequeue()];
|
|
|
|
Log.LogMessage(MessageImportance.High, workItem.Message);
|
|
|
|
|
|
|
|
try {
|
|
|
|
var proc = Process.Start(workItem.ProcessStartInfo);
|
|
|
|
proc.OutputDataReceived += (object sender, DataReceivedEventArgs e) =>
|
|
|
|
{
|
|
|
|
if (!string.IsNullOrEmpty(e.Data))
|
|
|
|
Log.LogMessage(MessageImportance.High, string.Join(" ", new[]
|
|
|
|
{
|
|
|
|
(QtDebug ? "[" + (((Process)sender).Id.ToString()) + "]" : ""),
|
|
|
|
e.Data
|
|
|
|
}));
|
|
|
|
};
|
|
|
|
proc.ErrorDataReceived += (object sender, DataReceivedEventArgs e) =>
|
|
|
|
{
|
|
|
|
if (!string.IsNullOrEmpty(e.Data))
|
|
|
|
Log.LogMessage(MessageImportance.High, string.Join(" ", new[]
|
|
|
|
{
|
|
|
|
(QtDebug ? "[" + (((Process)sender).Id.ToString()) + "]" : ""),
|
|
|
|
e.Data
|
|
|
|
}));
|
|
|
|
};
|
|
|
|
proc.BeginOutputReadLine();
|
|
|
|
proc.BeginErrorReadLine();
|
|
|
|
running.Enqueue(new KeyValuePair<string, Process>(workItem.Key, proc));
|
|
|
|
} catch (Exception e) {
|
|
|
|
Log.LogError(
|
|
|
|
string.Format("[QtRunWork] Error starting process {0}: {1}",
|
|
|
|
workItem.ToolPath, e.Message));
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
string qtDebugRunning = "";
|
|
|
|
if (QtDebug) {
|
|
|
|
qtDebugRunning = string.Format("## QtRunWork waiting {0}",
|
|
|
|
string.Join(", ", running
|
|
|
|
.Select(x => string.Format("{0} [{1}]", x.Key, x.Value.Id))));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for process to terminate when there are processes running, and...
|
|
|
|
while (ok && running.Any()
|
|
|
|
// ...work is queued but already reached the maximum number of processes, or...
|
|
|
|
&& ((workQueue.Any() && running.Count >= QtMaxProcs)
|
|
|
|
// ...work queue is empty but there are dependents that haven't yet been queued
|
|
|
|
|| (!workQueue.Any() && postponedItems.Any()))) {
|
|
|
|
|
|
|
|
var itemProc = running.Dequeue();
|
|
|
|
workItem = workItems[itemProc.Key];
|
|
|
|
var proc = itemProc.Value;
|
|
|
|
|
|
|
|
if (QtDebug && !string.IsNullOrEmpty(qtDebugRunning)) {
|
|
|
|
Log.LogMessage(MessageImportance.High, qtDebugRunning);
|
|
|
|
qtDebugRunning = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (proc.WaitForExit(100)) {
|
|
|
|
if (QtDebug) {
|
|
|
|
Log.LogMessage(MessageImportance.High,
|
|
|
|
string.Format("## QtRunWork exit {0} [{1}] = {2} ({3:0.00} msecs)",
|
|
|
|
workItem.Key, proc.Id, proc.ExitCode,
|
|
|
|
(proc.ExitTime - proc.StartTime).TotalMilliseconds));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process terminated; check exit code and close
|
|
|
|
terminated.Add(workItem.Key);
|
|
|
|
result[workItem.Key].SetMetadata("ExitCode", proc.ExitCode.ToString());
|
|
|
|
ok &= (proc.ExitCode == 0);
|
|
|
|
proc.Close();
|
|
|
|
|
|
|
|
// Add postponed dependent items to work queue
|
|
|
|
if (ok && dependentsOf.ContainsKey(workItem.Key)) {
|
|
|
|
// Dependents of workItem...
|
|
|
|
var readyDependents = dependentsOf[workItem.Key]
|
|
|
|
// ...that have not yet been queued...
|
|
|
|
.Where(x => postponedItems.Contains(x)
|
|
|
|
// ...and whose dependending items have all terminated.
|
|
|
|
&& workItems[x].DependsOn.All(y => terminated.Contains(y)));
|
|
|
|
|
|
|
|
if (QtDebug && readyDependents.Any()) {
|
|
|
|
Log.LogMessage(MessageImportance.High,
|
|
|
|
string.Format("## QtRunWork queueing\r\n## {0}",
|
|
|
|
string.Join("\r\n## ", readyDependents)));
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var dependent in readyDependents) {
|
|
|
|
postponedItems.Remove(dependent);
|
|
|
|
workQueue.Enqueue(dependent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Process is still running; feed it back into the running queue
|
|
|
|
running.Enqueue(itemProc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there are items still haven't been queued, that means a circular dependency exists
|
|
|
|
if (ok && postponedItems.Any()) {
|
|
|
|
ok = false;
|
|
|
|
Log.LogError("[QtRunWork] Error: circular dependency");
|
|
|
|
if (QtDebug) {
|
|
|
|
Log.LogMessage(MessageImportance.High,
|
|
|
|
string.Format("## QtRunWork circularity\r\n## {0}",
|
|
|
|
string.Join("\r\n## ", postponedItems
|
|
|
|
.Select(x => string.Format("{0} <- {1}", x,
|
|
|
|
string.Join(", ", workItems[x].DependsOn))))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ok && QtDebug) {
|
|
|
|
Log.LogMessage(MessageImportance.High,
|
|
|
|
"## QtRunWork all work queued");
|
|
|
|
if (running.Any()) {
|
|
|
|
Log.LogMessage(MessageImportance.High,
|
|
|
|
string.Format("## QtRunWork waiting {0}",
|
|
|
|
string.Join(", ", running
|
|
|
|
.Select(x => string.Format("{0} [{1}]", x.Key, x.Value.Id)))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for all running processes to terminate
|
|
|
|
while (running.Any()) {
|
|
|
|
var itemProc = running.Dequeue();
|
|
|
|
var workItem = workItems[itemProc.Key];
|
|
|
|
var proc = itemProc.Value;
|
|
|
|
if (proc.WaitForExit(100)) {
|
|
|
|
if (QtDebug) {
|
|
|
|
Log.LogMessage(MessageImportance.High,
|
|
|
|
string.Format("## QtRunWork exit {0} [{1}] = {2} ({3:0.00} msecs)",
|
|
|
|
workItem.Key, proc.Id, proc.ExitCode,
|
|
|
|
(proc.ExitTime - proc.StartTime).TotalMilliseconds));
|
|
|
|
}
|
|
|
|
// Process terminated; check exit code and close
|
|
|
|
result[workItem.Key].SetMetadata("ExitCode", proc.ExitCode.ToString());
|
|
|
|
ok &= (proc.ExitCode == 0);
|
|
|
|
proc.Close();
|
|
|
|
} else {
|
|
|
|
// Process is still running; feed it back into the running queue
|
|
|
|
running.Enqueue(itemProc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (QtDebug) {
|
|
|
|
Log.LogMessage(MessageImportance.High,
|
|
|
|
string.Format("## QtRunWork result {0}", (ok ? "ok" : "FAILED!")));
|
|
|
|
}
|
|
|
|
|
|
|
|
Result = result.Values.ToArray();
|
|
|
|
if (!ok)
|
|
|
|
return false;
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// TASK ParseVarDefs
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// -->
|
|
|
|
<UsingTask TaskName="ParseVarDefs"
|
|
|
|
TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<QtVars Required="true"
|
|
|
|
ParameterType="System.String"/>
|
|
|
|
<OutVarDefs Output="true"
|
|
|
|
ParameterType="Microsoft.Build.Framework.ITaskItem[]"/>
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Using Namespace="System"/>
|
|
|
|
<Using Namespace="System.Text"/>
|
|
|
|
<Using Namespace="System.Text.RegularExpressions"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
OutVarDefs = Regex.Matches(QtVars,
|
|
|
|
@"\s*(\w+)\s*(?:;|=\s*(\w*)\s*(?:\/((?:\\.|[^;\/])*)\/((?:\\.|[^;\/])*)\/)?)?")
|
|
|
|
.Cast<Match>()
|
|
|
|
.Where((Match x) => x.Groups.Count > 4 && !string.IsNullOrEmpty(x.Groups[1].Value))
|
|
|
|
.Select((Match x) => x.Groups
|
|
|
|
.Cast<Group>()
|
|
|
|
.Select((Group y) => !string.IsNullOrEmpty(y.Value) ? y.Value : null)
|
|
|
|
.ToArray())
|
|
|
|
.Select((string[] x) => new TaskItem(x[1],
|
|
|
|
new Dictionary<string,string>
|
|
|
|
{
|
|
|
|
{ "Name" , x[2] ?? x[1] },
|
|
|
|
{ "Pattern" , x[3] ?? ".*" },
|
|
|
|
{ "Value" , x[4] ?? "$0" },
|
|
|
|
}))
|
|
|
|
.ToArray();
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// TASK GetVarsFromMakefile
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// -->
|
|
|
|
<UsingTask TaskName="GetVarsFromMakefile"
|
|
|
|
TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<Makefile Required="true"
|
|
|
|
ParameterType="System.String"/>
|
|
|
|
<VarDefs Required="false"
|
|
|
|
ParameterType="Microsoft.Build.Framework.ITaskItem[]"/>
|
|
|
|
<ExcludeValues Required="true"
|
|
|
|
ParameterType="System.String[]"/>
|
|
|
|
<OutVars Output="true"
|
|
|
|
ParameterType="Microsoft.Build.Framework.ITaskItem[]"/>
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Using Namespace="System"/>
|
|
|
|
<Using Namespace="System.Text"/>
|
|
|
|
<Using Namespace="System.Text.RegularExpressions"/>
|
|
|
|
<Using Namespace="System.IO"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
var makefileVars = Regex.Matches(
|
|
|
|
File.ReadAllText(Makefile),
|
|
|
|
@"^(\w+)[^\=\r\n\S]*\=[^\r\n\S]*([^\r\n]+)[\r\n]",
|
|
|
|
RegexOptions.Multiline).Cast<Match>()
|
|
|
|
.Where(x => x.Groups.Count > 2 && x.Groups[1].Success && x.Groups[2].Success
|
|
|
|
&& !string.IsNullOrEmpty(x.Groups[1].Value))
|
|
|
|
.GroupBy(x => x.Groups[1].Value)
|
|
|
|
.ToDictionary(g => g.Key, g => g.Last().Groups[2].Value);
|
|
|
|
OutVars = VarDefs
|
|
|
|
.Where(x => makefileVars.ContainsKey(x.GetMetadata("Name")))
|
|
|
|
.Select(x => new TaskItem(x.ItemSpec, new Dictionary<string,string>
|
|
|
|
{ {
|
|
|
|
"Value",
|
|
|
|
string.Join(";", Regex
|
|
|
|
.Matches(makefileVars[x.GetMetadata("Name")], x.GetMetadata("Pattern"))
|
|
|
|
.Cast<Match>()
|
|
|
|
.Select(y => Regex
|
|
|
|
.Replace(y.Value, x.GetMetadata("Pattern"), x.GetMetadata("Value")))
|
|
|
|
.Where(y => !string.IsNullOrEmpty(y)
|
|
|
|
&& !ExcludeValues.Contains(y,
|
|
|
|
StringComparer.InvariantCultureIgnoreCase))
|
|
|
|
.ToHashSet())
|
|
|
|
} }))
|
|
|
|
.Where(x => !string.IsNullOrEmpty(x.GetMetadata("Value")))
|
|
|
|
.ToArray();
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// TASK Flatten
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Destructure items into a "flat" list of metadata. The output is a list of (Name, Value) pairs,
|
|
|
|
// each corresponding to one item metadata. Semi-colon-separated lists will also expand to many
|
|
|
|
// items in the output list, with the metadata name shared among them.
|
|
|
|
// Example:
|
|
|
|
// INPUT:
|
|
|
|
// <QtMoc>
|
|
|
|
// <InputFile>foo.h</InputFile>
|
|
|
|
// <IncludePath>C:\FOO;D:\BAR</IncludePath>
|
|
|
|
// </QtMoc>
|
|
|
|
// OUTPUT:
|
|
|
|
// <Result>
|
|
|
|
// <Name>InputFile</Name>
|
|
|
|
// <Value>foo.h</Value>
|
|
|
|
// </Result>
|
|
|
|
// <Result>
|
|
|
|
// <Name>IncludePath</Name>
|
|
|
|
// <Value>C:\FOO</Value>
|
|
|
|
// </Result>
|
|
|
|
// <Result>
|
|
|
|
// <Name>IncludePath</Name>
|
|
|
|
// <Value>D:\BAR</Value>
|
|
|
|
// </Result>
|
|
|
|
// Parameters:
|
|
|
|
// in ITaskItem[] Items: list of items to flatten
|
|
|
|
// in string[] Metadata: names of metadata to look for; omit to include all metadata
|
|
|
|
// out ITaskItem[] Result: list of metadata from all items
|
|
|
|
// -->
|
|
|
|
<UsingTask TaskName="Flatten"
|
|
|
|
TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<Items ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
|
|
|
|
<Metadata ParameterType="System.String[]" Required="false" />
|
|
|
|
<Result ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Using Namespace="System"/>
|
|
|
|
<Using Namespace="System.IO"/>
|
|
|
|
<Using Namespace="System.Diagnostics"/>
|
|
|
|
<Using Namespace="System.Collections.Generic"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
Result = new ITaskItem[] { };
|
|
|
|
var reserved = new HashSet<string>
|
|
|
|
{
|
|
|
|
"AccessedTime", "CreatedTime", "DefiningProjectDirectory",
|
|
|
|
"DefiningProjectExtension", "DefiningProjectFullPath", "DefiningProjectName",
|
|
|
|
"Directory", "Extension", "Filename", "FullPath", "Identity", "ModifiedTime",
|
|
|
|
"RecursiveDir", "RelativeDir", "RootDir",
|
|
|
|
};
|
|
|
|
if (Metadata == null)
|
|
|
|
Metadata = new string[0];
|
|
|
|
var requestedNames = new HashSet<string>(Metadata.Where(x => !string.IsNullOrEmpty(x)));
|
|
|
|
var newItems = new List<ITaskItem>();
|
|
|
|
foreach (var item in Items) {
|
|
|
|
var itemName = item.ItemSpec;
|
|
|
|
var names = item.MetadataNames.Cast<string>().Where(x => !reserved.Contains(x)
|
|
|
|
&& (!requestedNames.Any() || requestedNames.Contains(x)));
|
|
|
|
foreach (string name in names) {
|
|
|
|
var values = item.GetMetadata(name).Split(';');
|
|
|
|
foreach (string value in values.Where(v => !string.IsNullOrEmpty(v))) {
|
|
|
|
newItems.Add(new TaskItem(string.Format("{0}={1}", name, value),
|
|
|
|
new Dictionary<string, string>
|
|
|
|
{
|
|
|
|
{ "Item", itemName },
|
|
|
|
{ "Name", name },
|
|
|
|
{ "Value", value },
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Result = newItems.ToArray();
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// TASK HostTranslatePaths
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Translate local (Windows) paths to build host paths. This could be a Linux host for cross
|
|
|
|
// compilation, or a simple copy (i.e. "no-op") when building in Windows.
|
|
|
|
// Input and output items are in the form:
|
|
|
|
// <...>
|
|
|
|
// <Item>...</Item>
|
|
|
|
// <Name>...</Name>
|
|
|
|
// <Value>...</Value>
|
|
|
|
// </...>
|
|
|
|
// where <Item> is the local path, <Name> is a filter criteria identifier matched with the Names
|
|
|
|
// parameter, and <Value> is set to the host path in output items (for input items <Value> must
|
|
|
|
// be equal to <Item>).
|
|
|
|
// Parameters:
|
|
|
|
// in ITaskItem[] Items: input items with local paths
|
|
|
|
// in string[] Names: filter criteria; unmatched items will simply be copied (i.e. no-op)
|
|
|
|
// out ITaskItem[] Result: output items with translated host paths
|
|
|
|
// -->
|
|
|
|
<!--// Linux build over WSL -->
|
|
|
|
<UsingTask TaskName="HostTranslatePaths" TaskFactory="CodeTaskFactory"
|
|
|
|
Condition="'$(ApplicationType)' == 'Linux' AND '$(PlatformToolset)' == 'WSL_1_0'"
|
|
|
|
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<Items ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
|
|
|
|
<Names ParameterType="System.String[]" Required="false" />
|
|
|
|
<Result ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Reference Include="$(VCTargetsPath)\Application Type\Linux\1.0\liblinux.dll"/>
|
|
|
|
<Using Namespace="System.IO"/>
|
|
|
|
<Using Namespace="liblinux"/>
|
|
|
|
<Using Namespace="liblinux.IO"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
Result = new ITaskItem[] { };
|
|
|
|
var newItems = new List<ITaskItem>();
|
|
|
|
foreach (var item in Items) {
|
|
|
|
string itemName = item.GetMetadata("Name");
|
|
|
|
string itemValue = item.GetMetadata("Value");
|
|
|
|
if (Names.Contains(itemName)) {
|
|
|
|
if (Path.IsPathRooted(itemValue) && !itemValue.StartsWith("/"))
|
|
|
|
itemValue = PathUtils.TranslateWindowsPathToWSLPath(itemValue);
|
|
|
|
else
|
|
|
|
itemValue = itemValue.Replace(@"\", "/");
|
|
|
|
}
|
|
|
|
newItems.Add(new TaskItem(item.ItemSpec,
|
|
|
|
new Dictionary<string, string>
|
|
|
|
{
|
|
|
|
{ "Item", item.GetMetadata("Item") },
|
|
|
|
{ "Name", itemName },
|
|
|
|
{ "Value", itemValue },
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
Result = newItems.ToArray();
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
<!--// Linux build over SSH -->
|
|
|
|
<UsingTask TaskName="HostTranslatePaths" TaskFactory="CodeTaskFactory"
|
|
|
|
Condition="'$(ApplicationType)' == 'Linux' AND '$(PlatformToolset)' != 'WSL_1_0'"
|
|
|
|
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<Items ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
|
|
|
|
<Names ParameterType="System.String[]" Required="false" />
|
|
|
|
<Result ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Reference
|
|
|
|
Include="$(VCTargetsPath)\Application Type\Linux\1.0\Microsoft.Build.Linux.Tasks.dll"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
Result = new ITaskItem[] { };
|
|
|
|
var newItems = new List<ITaskItem>();
|
|
|
|
foreach (var item in Items) {
|
|
|
|
string itemName = item.GetMetadata("Name");
|
|
|
|
string itemValue = item.GetMetadata("Value");
|
|
|
|
if (Names.Contains(itemName)) {
|
|
|
|
if (Path.IsPathRooted(itemValue) && !itemValue.StartsWith("/")) {
|
|
|
|
var projectdir = new Uri(@"$(ProjectDir)");
|
|
|
|
var itemFileName = Path.GetFileName(itemValue);
|
|
|
|
var itemDirName = Path.GetFullPath(Path.GetDirectoryName(itemValue));
|
|
|
|
if (!itemDirName.EndsWith(@"\"))
|
|
|
|
itemDirName += @"\";
|
|
|
|
var itemDir = new Uri(itemDirName);
|
|
|
|
if (projectdir.IsBaseOf(itemDir)) {
|
|
|
|
itemValue = projectdir.MakeRelativeUri(itemDir).OriginalString
|
|
|
|
+ itemFileName;
|
|
|
|
} else {
|
|
|
|
Log.LogWarning("Unable to translate path: {0}", itemValue);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
itemValue = itemValue.Replace(@"\", "/");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
newItems.Add(new TaskItem(item.ItemSpec,
|
|
|
|
new Dictionary<string, string>
|
|
|
|
{
|
|
|
|
{ "Item", item.GetMetadata("Item") },
|
|
|
|
{ "Name", itemName },
|
|
|
|
{ "Value", itemValue },
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
Result = newItems.ToArray();
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
<!--// Local (Windows) build -->
|
|
|
|
<UsingTask TaskName="HostTranslatePaths" TaskFactory="CodeTaskFactory"
|
|
|
|
Condition="'$(ApplicationType)' != 'Linux'"
|
|
|
|
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<Items ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
|
|
|
|
<Names ParameterType="System.String[]" Required="false" />
|
|
|
|
<Result ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
Result = Items.ToArray();
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// TASK HostExec
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Run command in build host.
|
|
|
|
// Parameters:
|
|
|
|
// in string Command: Command to run on the build host
|
|
|
|
// in string RedirectStdOut: Path to file to receive redirected output messages
|
|
|
|
// * can be NUL to discard messages
|
|
|
|
// in string RedirectStdErr: Path to file to receive redirected error messages
|
|
|
|
// * can be NUL to discard messages
|
|
|
|
// * can be STDOUT to apply the same redirection as output messages
|
|
|
|
// in string WorkingDirectory: Path to directory where command will be run
|
|
|
|
// in ITaskItem[] Inputs: List of local -> host path mappings for command inputs
|
|
|
|
// * item format: cf. HostTranslatePaths task
|
|
|
|
// in ITaskItem[] Outputs: List of host -> local path mappings for command outputs
|
|
|
|
// * item format: cf. HostTranslatePaths task
|
|
|
|
// in string RemoteTarget: Set by ResolveRemoteDir in SSH mode; null otherwise
|
|
|
|
// in string RemoteProjectDir: Set by ResolveRemoteDir in SSH mode; null otherwise
|
|
|
|
// in bool IgnoreExitCode: Set flag to disable build error if command failed
|
|
|
|
// out int ExitCode: status code at command exit
|
|
|
|
// -->
|
|
|
|
<!--// Linux build over WSL -->
|
|
|
|
<UsingTask TaskName="HostExec" TaskFactory="CodeTaskFactory"
|
|
|
|
Condition="'$(ApplicationType)' == 'Linux' AND '$(PlatformToolset)' == 'WSL_1_0'"
|
|
|
|
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<!--IN-->
|
|
|
|
<Message ParameterType="System.String" Required="false" />
|
|
|
|
<Command ParameterType="System.String" Required="true" />
|
|
|
|
<RedirectStdOut ParameterType="System.String" Required="false" />
|
|
|
|
<RedirectStdErr ParameterType="System.String" Required="false" />
|
|
|
|
<WorkingDirectory ParameterType="System.String" Required="false" />
|
|
|
|
<Inputs ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" />
|
|
|
|
<Outputs ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" />
|
|
|
|
<RemoteTarget ParameterType="System.String" Required="false" />
|
|
|
|
<RemoteProjectDir ParameterType="System.String" Required="false" />
|
|
|
|
<IgnoreExitCode ParameterType="System.Boolean" Required="false" />
|
|
|
|
<!--OUT-->
|
|
|
|
<ExitCode ParameterType="System.Int32" Output="true" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Reference
|
|
|
|
Include="$(VCTargetsPath)\Application Type\Linux\1.0\Microsoft.Build.Linux.Tasks.dll"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
if (!string.IsNullOrEmpty(Message))
|
|
|
|
Log.LogMessage(MessageImportance.High, Message);
|
|
|
|
Command = "(" + Command + ")";
|
|
|
|
if (RedirectStdOut == "NUL" || RedirectStdOut == "/dev/null")
|
|
|
|
Command += " 1> /dev/null";
|
|
|
|
else if (!string.IsNullOrEmpty(RedirectStdOut))
|
|
|
|
Command += " 1> " + RedirectStdOut;
|
|
|
|
if (RedirectStdErr == "NUL" || RedirectStdErr == "/dev/null")
|
|
|
|
Command += " 2> /dev/null";
|
|
|
|
else if (RedirectStdErr == "STDOUT")
|
|
|
|
Command += " 2>&1";
|
|
|
|
else if (!string.IsNullOrEmpty(RedirectStdErr))
|
|
|
|
Command += " 2> " + RedirectStdErr;
|
|
|
|
|
|
|
|
var createDirs = new List<string>();
|
|
|
|
if (Inputs != null) {
|
|
|
|
createDirs.AddRange(Inputs
|
|
|
|
.Select(x => string.Format("\x24(dirname {0})", x.GetMetadata("Value"))));
|
|
|
|
}
|
|
|
|
if (Outputs != null) {
|
|
|
|
createDirs.AddRange(Outputs
|
|
|
|
.Select(x => string.Format("\x24(dirname {0})", x.GetMetadata("Value"))));
|
|
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(WorkingDirectory)) {
|
|
|
|
createDirs.Add(WorkingDirectory);
|
|
|
|
Command = string.Format("cd {0}; {1}", WorkingDirectory, Command);
|
|
|
|
}
|
|
|
|
if (createDirs.Any()) {
|
|
|
|
Command = string.Format("{0}; {1}",
|
|
|
|
string.Join("; ", createDirs.Select(x => string.Format("mkdir -p {0}", x))),
|
|
|
|
Command);
|
|
|
|
}
|
|
|
|
|
|
|
|
var taskExec = new Microsoft.Build.Linux.WSL.Tasks.ExecuteCommand()
|
|
|
|
{
|
|
|
|
BuildEngine = BuildEngine,
|
|
|
|
HostObject = HostObject,
|
|
|
|
ProjectDir = @"$(ProjectDir)",
|
|
|
|
IntermediateDir = @"$(IntDir)",
|
|
|
|
WSLPath = @"$(WSLPath)",
|
|
|
|
Command = Command,
|
|
|
|
};
|
|
|
|
Log.LogMessage("\r\n==== HostExec: Microsoft.Build.Linux.WSL.Tasks.ExecuteCommand");
|
|
|
|
Log.LogMessage("ProjectDir: {0}", taskExec.ProjectDir);
|
|
|
|
Log.LogMessage("IntermediateDir: {0}", taskExec.IntermediateDir);
|
|
|
|
Log.LogMessage("WSLPath: {0}", taskExec.WSLPath);
|
|
|
|
Log.LogMessage("Command: {0}", taskExec.Command);
|
|
|
|
|
|
|
|
bool ok = taskExec.Execute();
|
|
|
|
Log.LogMessage("== {0} ExitCode: {1}\r\n", ok ? "OK" : "FAIL", taskExec.ExitCode);
|
|
|
|
|
|
|
|
ExitCode = taskExec.ExitCode;
|
|
|
|
if (!ok && !IgnoreExitCode) {
|
|
|
|
Log.LogError("Host command failed.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
<!--// Linux build over SSH -->
|
|
|
|
<UsingTask TaskName="HostExec" TaskFactory="CodeTaskFactory"
|
|
|
|
Condition="'$(ApplicationType)' == 'Linux' AND '$(PlatformToolset)' != 'WSL_1_0'"
|
|
|
|
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<!--IN-->
|
|
|
|
<Message ParameterType="System.String" Required="false" />
|
|
|
|
<Command ParameterType="System.String" Required="true" />
|
|
|
|
<RedirectStdOut ParameterType="System.String" Required="false" />
|
|
|
|
<RedirectStdErr ParameterType="System.String" Required="false" />
|
|
|
|
<WorkingDirectory ParameterType="System.String" Required="false" />
|
|
|
|
<Inputs ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" />
|
|
|
|
<Outputs ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" />
|
|
|
|
<RemoteTarget ParameterType="System.String" Required="false" />
|
|
|
|
<RemoteProjectDir ParameterType="System.String" Required="false" />
|
|
|
|
<IgnoreExitCode ParameterType="System.Boolean" Required="false" />
|
|
|
|
<!--OUT-->
|
|
|
|
<ExitCode ParameterType="System.Int32" Output="true" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Reference
|
|
|
|
Include="$(VCTargetsPath)\Application Type\Linux\1.0\Microsoft.Build.Linux.Tasks.dll"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
if (!string.IsNullOrEmpty(Message))
|
|
|
|
Log.LogMessage(MessageImportance.High, Message);
|
|
|
|
var createDirs = new List<string>
|
|
|
|
{
|
|
|
|
string.Format("{0}/{1}", RemoteProjectDir, WorkingDirectory)
|
|
|
|
};
|
|
|
|
|
|
|
|
var localFilesToCopyRemotelyMapping = new string[0];
|
|
|
|
if (Inputs != null) {
|
|
|
|
localFilesToCopyRemotelyMapping = Inputs
|
|
|
|
.Select(x => string.Format(@"{0}:={1}/{2}",
|
|
|
|
x.GetMetadata("Item"),
|
|
|
|
RemoteProjectDir,
|
|
|
|
x.GetMetadata("Value")))
|
|
|
|
.ToArray();
|
|
|
|
createDirs.AddRange(Inputs
|
|
|
|
.Select(x => string.Format("\x24(dirname {0})", x.GetMetadata("Value"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
var remoteFilesToCopyLocallyMapping = new string[0];
|
|
|
|
if (Outputs != null) {
|
|
|
|
remoteFilesToCopyLocallyMapping = Outputs
|
|
|
|
.Select(x => string.Format(@"{0}/{1}:={2}",
|
|
|
|
RemoteProjectDir,
|
|
|
|
x.GetMetadata("Value"),
|
|
|
|
x.GetMetadata("Item")))
|
|
|
|
.ToArray();
|
|
|
|
createDirs.AddRange(Outputs
|
|
|
|
.Select(x => string.Format("\x24(dirname {0})", x.GetMetadata("Value"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
Command = "(" + Command + ")";
|
|
|
|
if (RedirectStdOut == "NUL" || RedirectStdOut == "/dev/null")
|
|
|
|
Command += " 1> /dev/null";
|
|
|
|
else if (!string.IsNullOrEmpty(RedirectStdOut))
|
|
|
|
Command += " 1> " + RedirectStdOut;
|
|
|
|
if (RedirectStdErr == "NUL" || RedirectStdErr == "/dev/null")
|
|
|
|
Command += " 2> /dev/null";
|
|
|
|
else if (RedirectStdErr == "STDOUT")
|
|
|
|
Command += " 2>&1";
|
|
|
|
else if (!string.IsNullOrEmpty(RedirectStdErr))
|
|
|
|
Command += " 2> " + RedirectStdErr;
|
|
|
|
Command = string.Format("cd {0}/{1}; {2}", RemoteProjectDir, WorkingDirectory, Command);
|
|
|
|
|
|
|
|
var taskCopyFiles = new Microsoft.Build.Linux.Tasks.Execute()
|
|
|
|
{
|
|
|
|
BuildEngine = BuildEngine,
|
|
|
|
HostObject = HostObject,
|
|
|
|
ProjectDir = @"$(ProjectDir)",
|
|
|
|
IntermediateDir = @"$(IntDir)",
|
|
|
|
RemoteTarget = RemoteTarget,
|
|
|
|
RemoteProjectDir = RemoteProjectDir,
|
|
|
|
Command = string.Join("; ", createDirs.Select(x => string.Format("mkdir -p {0}", x))),
|
|
|
|
LocalFilesToCopyRemotelyMapping = localFilesToCopyRemotelyMapping,
|
|
|
|
};
|
|
|
|
var taskExec = new Microsoft.Build.Linux.Tasks.Execute()
|
|
|
|
{
|
|
|
|
BuildEngine = BuildEngine,
|
|
|
|
HostObject = HostObject,
|
|
|
|
ProjectDir = @"$(ProjectDir)",
|
|
|
|
IntermediateDir = @"$(IntDir)",
|
|
|
|
RemoteTarget = RemoteTarget,
|
|
|
|
RemoteProjectDir = RemoteProjectDir,
|
|
|
|
Command = Command,
|
|
|
|
RemoteFilesToCopyLocallyMapping = remoteFilesToCopyLocallyMapping,
|
|
|
|
};
|
|
|
|
|
|
|
|
Log.LogMessage("\r\n==== HostExec: Microsoft.Build.Linux.Tasks.Execute");
|
|
|
|
Log.LogMessage("ProjectDir: {0}", taskExec.ProjectDir);
|
|
|
|
Log.LogMessage("IntermediateDir: {0}", taskExec.IntermediateDir);
|
|
|
|
Log.LogMessage("RemoteTarget: {0}", taskExec.RemoteTarget);
|
|
|
|
Log.LogMessage("RemoteProjectDir: {0}", taskExec.RemoteProjectDir);
|
|
|
|
if (taskExec.LocalFilesToCopyRemotelyMapping.Any())
|
|
|
|
Log.LogMessage("LocalFilesToCopyRemotelyMapping: {0}",
|
|
|
|
taskExec.LocalFilesToCopyRemotelyMapping);
|
|
|
|
if (taskExec.RemoteFilesToCopyLocallyMapping.Any())
|
|
|
|
Log.LogMessage("RemoteFilesToCopyLocallyMapping: {0}",
|
|
|
|
taskExec.RemoteFilesToCopyLocallyMapping);
|
|
|
|
Log.LogMessage("CreateDirs: {0}", taskCopyFiles.Command);
|
|
|
|
Log.LogMessage("Command: {0}", taskExec.Command);
|
|
|
|
|
|
|
|
if (!taskCopyFiles.ExecuteTool())
|
|
|
|
return false;
|
|
|
|
bool ok = taskExec.ExecuteTool();
|
|
|
|
Log.LogMessage("== {0} ExitCode: {1}\r\n", ok ? "OK" : "FAIL", taskExec.ExitCode);
|
|
|
|
|
|
|
|
ExitCode = taskExec.ExitCode;
|
|
|
|
if (!ok && !IgnoreExitCode) {
|
|
|
|
Log.LogError("Host command failed.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
<!--// Local (Windows) build -->
|
|
|
|
<UsingTask TaskName="HostExec" TaskFactory="CodeTaskFactory"
|
|
|
|
Condition="'$(ApplicationType)' != 'Linux'"
|
|
|
|
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
|
|
|
|
<ParameterGroup>
|
|
|
|
<!--IN-->
|
|
|
|
<Message ParameterType="System.String" Required="false" />
|
|
|
|
<Command ParameterType="System.String" Required="true" />
|
|
|
|
<RedirectStdOut ParameterType="System.String" Required="false" />
|
|
|
|
<RedirectStdErr ParameterType="System.String" Required="false" />
|
|
|
|
<WorkingDirectory ParameterType="System.String" Required="false" />
|
|
|
|
<Inputs ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" />
|
|
|
|
<Outputs ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" />
|
|
|
|
<RemoteTarget ParameterType="System.String" Required="false" />
|
|
|
|
<RemoteProjectDir ParameterType="System.String" Required="false" />
|
|
|
|
<IgnoreExitCode ParameterType="System.Boolean" Required="false" />
|
|
|
|
<!--OUT-->
|
|
|
|
<ExitCode ParameterType="System.Int32" Output="true" />
|
|
|
|
</ParameterGroup>
|
|
|
|
<Task>
|
|
|
|
<Reference Include="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll"/>
|
|
|
|
<Reference Include="$(MSBuildToolsPath)\Microsoft.Build.Utilities.Core.dll"/>
|
|
|
|
<Code Type="Fragment" Language="cs">
|
|
|
|
<![CDATA[
|
|
|
|
if (!string.IsNullOrEmpty(Message))
|
|
|
|
Log.LogMessage(MessageImportance.High, Message);
|
|
|
|
Command = "(" + Command + ")";
|
|
|
|
if (RedirectStdOut == "NUL" || RedirectStdOut == "/dev/null")
|
|
|
|
Command += " 1> NUL";
|
|
|
|
else if (!string.IsNullOrEmpty(RedirectStdOut))
|
|
|
|
Command += " 1> " + RedirectStdOut;
|
|
|
|
if (RedirectStdErr == "NUL" || RedirectStdErr == "/dev/null")
|
|
|
|
Command += " 2> NUL";
|
|
|
|
else if (RedirectStdErr == "STDOUT")
|
|
|
|
Command += " 2>&1";
|
|
|
|
else if (!string.IsNullOrEmpty(RedirectStdErr))
|
|
|
|
Command += " 2> " + RedirectStdErr;
|
|
|
|
|
|
|
|
var taskExec = new Microsoft.Build.Tasks.Exec()
|
|
|
|
{
|
|
|
|
BuildEngine = BuildEngine,
|
|
|
|
HostObject = HostObject,
|
|
|
|
WorkingDirectory = WorkingDirectory,
|
|
|
|
Command = Command,
|
|
|
|
IgnoreExitCode = IgnoreExitCode,
|
|
|
|
};
|
|
|
|
|
|
|
|
Log.LogMessage("\r\n==== HostExec: Microsoft.Build.Tasks.Exec");
|
|
|
|
Log.LogMessage("WorkingDirectory: {0}", taskExec.WorkingDirectory);
|
|
|
|
Log.LogMessage("Command: {0}", taskExec.Command);
|
|
|
|
|
|
|
|
bool ok = taskExec.Execute();
|
|
|
|
Log.LogMessage("== {0} ExitCode: {1}\r\n", ok ? "OK" : "FAIL", taskExec.ExitCode);
|
|
|
|
|
|
|
|
ExitCode = taskExec.ExitCode;
|
|
|
|
if (!ok)
|
|
|
|
return false;
|
|
|
|
]]>
|
|
|
|
</Code>
|
|
|
|
</Task>
|
|
|
|
</UsingTask>
|
|
|
|
</Project>
|