public ToolFlags CopyAndBuildFlags(BSPBuilder bsp, List <string> projectFiles, string subdir, ref PropertyList configurableProperties, ReverseFileConditionBuilder.Handle reverseConditions)
{
List <ParsedCondition> conditions = null;
List <ConditionRecord> allConditions = new List <ConditionRecord>();
List <string> preprocessorMacros = new List <string>();
if (SimpleFileConditions != null)
{
allConditions.AddRange(SimpleFileConditions.Select(c => new ConditionRecord(c, null)));
reverseConditions?.FlagIncomplete(ReverseFileConditionWarning.HasRegularConditions);
}
if (!string.IsNullOrEmpty(PreprocessorMacros))
{
preprocessorMacros.AddRange(PreprocessorMacros.Split(';'));
foreach (var macro in preprocessorMacros)
{
reverseConditions?.AttachPreprocessorMacro(macro, null);
}
}
if (SmartFileConditions != null || SmartPreprocessorMacros != null)
{
PropertyGroup grp;
if (configurableProperties == null)
{
configurableProperties = new PropertyList {
PropertyGroups = new List <PropertyGroup>()
}
}
;
if (!string.IsNullOrEmpty(SmartPropertyGroup))
{
string[] elements = SmartPropertyGroup.Split('|');
configurableProperties.PropertyGroups.Add(grp = new PropertyGroup {
Name = elements[1], UniqueID = elements[0]
});
}
else
{
grp = configurableProperties.PropertyGroups.FirstOrDefault();
if (grp == null)
{
configurableProperties.PropertyGroups.Insert(0, grp = new PropertyGroup {
});
}
}
foreach (var str in SmartFileConditions ?? new string[0])
{
var def = SmartPropertyDefinition.Parse(str, grp.UniqueID);
if (def.Items.Length == 1)
{
var item = def.Items[0];
allConditions.Add(new ConditionRecord($"{item.Key}: $${def.IDWithPrefix}$$ == {item.Value.ID}", reverseConditions?.CreateSimpleCondition(def.IDWithPrefix, item.Value.ID)));
reverseConditions?.AttachMinimalConfigurationValue(def.IDWithPrefix, "");
grp.Properties.Add(new PropertyEntry.Boolean
{
ValueForTrue = item.Value.ID,
Name = def.Name,
UniqueID = def.IDWithoutPrefix,
DefaultValue = def.IsDefaultOn != false
});
}
else
{
List <PropertyEntry.Enumerated.Suggestion> suggestions = new List <PropertyEntry.Enumerated.Suggestion>();
int?emptyIndex = null;
foreach (var item in def.Items)
{
if (item.Key == "")
{
// 'None' value. No conditions will trigger when this is selected.
emptyIndex = suggestions.Count;
}
else
{
allConditions.Add(new ConditionRecord($"{item.Key}: $${def.IDWithPrefix}$$ == {item.Value.ID}", reverseConditions?.CreateSimpleCondition(def.IDWithPrefix, item.Value.ID)));
}
suggestions.Add(new PropertyEntry.Enumerated.Suggestion {
InternalValue = item.Value.ID, UserFriendlyName = item.Value.Name
});
}
reverseConditions?.AttachMinimalConfigurationValue(def.IDWithPrefix, suggestions[emptyIndex ?? def.DefaultItemIndex].InternalValue);
grp.Properties.Add(new PropertyEntry.Enumerated
{
Name = def.Name,
UniqueID = def.IDWithoutPrefix,
SuggestionList = suggestions.ToArray(),
DefaultEntryIndex = def.DefaultItemIndex
});
}
}
foreach (var str in SmartPreprocessorMacros ?? new string[0])
{
var def = SmartPropertyDefinition.Parse(str, grp.UniqueID, 1);
preprocessorMacros.Add(string.Format(def.ExtraArguments[0], "$$" + def.IDWithPrefix + "$$"));
if (def.Items.Length == 1)
{
var item = def.Items[0];
if (item.Key.StartsWith("@"))
{
var prop = new PropertyEntry.String
{
Name = def.Name,
UniqueID = def.IDWithoutPrefix,
DefaultValue = item.Key.TrimStart('@'),
};
grp.Properties.Add(prop);
reverseConditions?.AttachFreeformPreprocessorMacro(def.ExtraArguments[0], def.IDWithPrefix);
reverseConditions?.AttachMinimalConfigurationValue(def.IDWithPrefix, prop.DefaultValue);
}
else
{
grp.Properties.Add(new PropertyEntry.Boolean
{
Name = def.Name,
UniqueID = def.IDWithoutPrefix,
ValueForTrue = item.Key,
DefaultValue = def.IsDefaultOn ?? true,
});
string expandedMacro = string.Format(def.ExtraArguments[0], item.Key);
reverseConditions?.AttachPreprocessorMacro(expandedMacro, reverseConditions?.CreateSimpleCondition(def.IDWithPrefix, item.Key));
reverseConditions?.AttachMinimalConfigurationValue(def.IDWithPrefix, "");
}
}
else
{
List <PropertyEntry.Enumerated.Suggestion> suggestions = new List <PropertyEntry.Enumerated.Suggestion>();
foreach (var item in def.Items)
{
suggestions.Add(new PropertyEntry.Enumerated.Suggestion {
InternalValue = item.Key, UserFriendlyName = item.Value.Name
});
string expandedMacro = string.Format(def.ExtraArguments[0], item.Key);
reverseConditions?.AttachPreprocessorMacro(expandedMacro, reverseConditions?.CreateSimpleCondition(def.IDWithPrefix, item.Key));
}
reverseConditions?.AttachMinimalConfigurationValue(def.IDWithPrefix, suggestions[def.DefaultItemIndex].InternalValue);
grp.Properties.Add(new PropertyEntry.Enumerated
{
Name = def.Name,
UniqueID = def.IDWithoutPrefix,
SuggestionList = suggestions.ToArray(),
DefaultEntryIndex = def.DefaultItemIndex
});
}
}
}
if (allConditions.Count > 0)
{
conditions = new List <ParsedCondition>();
foreach (var cond in allConditions)
{
int idx = cond.Condition.IndexOf(':');
if (idx == -1)
{
throw new Exception("Invalid simple condition format");
}
Regex rgFile = new Regex(cond.Condition.Substring(0, idx), RegexOptions.IgnoreCase);
string rawCond = cond.Condition.Substring(idx + 1).Trim();
Condition parsedCond = ParseCondition(rawCond);
conditions.Add(new ParsedCondition {
Regex = rgFile, Condition = parsedCond, ReverseConditionHandle = cond.Handle
});
}
}
string expandedSourceFolder = SourceFolder;
bsp.ExpandVariables(ref expandedSourceFolder);
if (TargetFolder == null)
{
TargetFolder = Path.GetFileName(expandedSourceFolder);
}
TargetFolder = TargetFolder.Replace('\\', '/');
if (subdir == null)
{
subdir = "";
}
string absTarget = Path.Combine(bsp.BSPRoot, subdir, TargetFolder);
Directory.CreateDirectory(absTarget);
string folderInsideBSPPrefix = TargetFolder;
if (!string.IsNullOrEmpty(subdir))
{
folderInsideBSPPrefix = subdir + "/" + TargetFolder;
}
folderInsideBSPPrefix = folderInsideBSPPrefix.Replace('\\', '/');
if (folderInsideBSPPrefix == "/")
{
folderInsideBSPPrefix = "";
}
else if (folderInsideBSPPrefix != "" && !folderInsideBSPPrefix.StartsWith("/"))
{
folderInsideBSPPrefix = "/" + folderInsideBSPPrefix;
}
var copyMasks = new CopyFilters(FilesToCopy);
var autoIncludes = new CopyFilters(AutoIncludeMask);
var potentialSymlinks = new CopyFilters(SymlinkResolutionMask);
var projectContents = new CopyFilters(ProjectInclusionMask);
var filesToCopy = Directory.GetFiles(expandedSourceFolder, "*", SearchOption.AllDirectories)
.Where(f => !bsp.SkipHiddenFiles || (File.GetAttributes(f) & FileAttributes.Hidden) != FileAttributes.Hidden)
.Select(f => f.Substring(expandedSourceFolder.Length + 1))
.Where(f => copyMasks.IsMatch(f))
.ToArray();
foreach (var dir in filesToCopy.Select(f => Path.Combine(absTarget, Path.GetDirectoryName(f))).Distinct())
{
Directory.CreateDirectory(dir);
}
List <IRenameRule> rules = new List <IRenameRule>();
foreach (var r in (RenameRules ?? "").Split(';').Select(s => s.Trim()).Where(s => s != ""))
{
int idx = r.IndexOf("=>");
rules.Add(new RenameRule {
OldName = r.Substring(0, idx), NewName = r.Substring(idx + 2)
});
}
foreach (var r in (AdvancedRenameRules ?? "").Split(';').Where(s => s != ""))
{
int idx = r.IndexOf("=>");
rules.Add(new AdvancedRenamingRule {
OldName = new Regex(r.Substring(0, idx), RegexOptions.IgnoreCase), NewNameFormat = r.Substring(idx + 2)
});
}
var includeDirs = filesToCopy.Where(f => autoIncludes.IsMatch(f)).Select(f => Path.GetDirectoryName(f).Replace('\\', '/')).Distinct().Select(d => "$$SYS:BSP_ROOT$$" + folderInsideBSPPrefix + (string.IsNullOrEmpty(d) ? "" : ("/" + d))).ToList();
foreach (var f in filesToCopy)
{
string renamedRelativePath = f;
string pathInsidePackage = Path.Combine(subdir, TargetFolder, f);
if (pathInsidePackage.Length > 170)
{
if (!bsp.OnFilePathTooLong(pathInsidePackage))
{
continue;
}
}
string targetFile = Path.Combine(absTarget, f);
string newName = rules?.FirstOrDefault(r => r.Matches(f))?.Apply(targetFile);
if (newName == null)
{
if (bsp.RenamedFileTable.TryGetValue(targetFile, out newName) || bsp.RenamedFileTable.TryGetValue(targetFile.Replace('/', '\\'), out newName))
{
}
}
if (newName != null)
{
var oldTargetFile = targetFile;
targetFile = Path.Combine(Path.GetDirectoryName(targetFile), newName);
renamedRelativePath = Path.Combine(Path.GetDirectoryName(renamedRelativePath), newName);
bsp.RenamedFileTable[oldTargetFile] = newName;
}
if (AlreadyCopied)
{
if (!File.Exists(targetFile))
{
throw new Exception(targetFile + " required by a copy job marked as 'Already Copied' does not exist");
}
}
else
{
bool resolved = false;
var absSourcePath = Path.Combine(expandedSourceFolder, f);
if (potentialSymlinks.IsMatch(f))
{
for (; ;)
{
var contents = File.ReadAllLines(absSourcePath);
if (contents.Length == 1 && File.Exists(Path.Combine(Path.GetDirectoryName(absSourcePath), contents[0])))
{
absSourcePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(absSourcePath), contents[0]));
}
else
{
break;
}
}
}
if (!resolved)
{
File.Copy(absSourcePath, targetFile, true);
}
}
File.SetAttributes(targetFile, File.GetAttributes(targetFile) & ~FileAttributes.ReadOnly);
string encodedPath = "$$SYS:BSP_ROOT$$" + folderInsideBSPPrefix + "/" + renamedRelativePath.Replace('\\', '/');
bool includedInProject = projectContents.IsMatch(f);
if (includedInProject)
{
projectFiles.Add(encodedPath.Replace('\\', '/'));
}
bool foundCondition = false;
if (conditions != null)
{
foreach (var cond in conditions)
{
if (cond.Regex.IsMatch(f))
{
bsp.MatchedFileConditions.Add(new FileCondition {
ConditionToInclude = cond.Condition, FilePath = encodedPath
});
cond.UseCount++;
if (includedInProject)
{
cond.ReverseConditionHandle?.AttachFile(encodedPath);
}
foundCondition = true;
break;
}
}
}
if (!foundCondition && includedInProject)
{
reverseConditions?.AttachFile(encodedPath);
}
}
if (AdditionalProjectFiles != null)
{
foreach (var spec in AdditionalProjectFiles.Split(';'))
{
string encodedPath = "$$SYS:BSP_ROOT$$" + folderInsideBSPPrefix + "/" + spec;
projectFiles.Add(encodedPath);
}
}
var unusedConditions = conditions?.Where(c => c.UseCount == 0)?.ToArray();
if ((unusedConditions?.Length ?? 0) != 0)
{
throw new Exception(string.Format("Found {0} unused conditions. Please recheck your rules.", unusedConditions.Length));
}
if (Patches != null)
{
foreach (var p in Patches)
{
foreach (var fn in p.FilePath.Split(';'))
{
List <string> allLines = File.ReadAllLines(Path.Combine(absTarget, fn)).ToList();
p.Apply(allLines);
string targetPath = p.TargetPath;
if (targetPath == null)
{
targetPath = fn;
}
Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(absTarget, targetPath)));
File.WriteAllLines(Path.Combine(absTarget, targetPath), allLines);
}
}
}
if (GuardedFiles != null)
{
foreach (var gf in GuardedFiles)
{
int idx = gf.IndexOf("=>");
Regex rgFile = new Regex(gf.Substring(0, idx));
string macro = gf.Substring(idx + 2);
var fn = Path.Combine(absTarget, filesToCopy.First(f => rgFile.IsMatch(f)));
List <string> lines = new List <string>(File.ReadAllLines(fn));
int i = 0;
//1. Find first #include
for (i = 0; i < lines.Count; i++)
{
if (lines[i].Trim().StartsWith("#include"))
{
break;
}
}
//2. Find first non-preprocessor line
for (; i < lines.Count; i++)
{
if (!string.IsNullOrWhiteSpace(lines[i]) && !lines[i].Trim().StartsWith("#include"))
{
break;
}
}
if (i == lines.Count)
{
throw new Exception("Cannot find a place to insert guard in " + fn);
}
lines.Insert(i, string.Format("#if defined({0}) && {0}", macro));
lines.Add("#endif //" + macro);
File.WriteAllLines(fn, lines);
}
}
if (AdditionalIncludeDirs != null)
{
foreach (var dir in AdditionalIncludeDirs.Split(';'))
{
var mappedDir = MapIncludeDir(absTarget, subdir, dir);
reverseConditions?.AttachIncludeDir(mappedDir);
includeDirs.Add(mappedDir);
}
}
return(new ToolFlags
{
PreprocessorMacros = (preprocessorMacros.Count == 0) ? null : preprocessorMacros.ToArray(),
IncludeDirectories = includeDirs.ToArray()
});
}