Add aggregatePackage module#80
Add aggregatePackage module#80kingerja merged 10 commits intoCommonBuildToolset:masterfrom kingerja:iss60
Conversation
| <?xml version="1.0" encoding="utf-8"?> | ||
| <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
| <PropertyGroup Label="Configuration"> | ||
| <Description>Provides Global Package expansion for CBT.Nuget.</Description> |
|
|
||
| <PropertyGroup> | ||
| <RestoreNuGetPackagesDependsOn>$(RestoreNuGetPackagesDependsOn);AggregateNugetPackages</RestoreNuGetPackagesDependsOn> | ||
| <RestoreDependsOn>$(RestoreDependsOn);AggregateNugetPackages</RestoreDependsOn> |
There was a problem hiding this comment.
You shouldn't need this if you're setting RestoreNuGetPackagesDependsOn. If it needs to be set, I'd say that's a bug.
There was a problem hiding this comment.
This appears to be an issue in the globalpackages as well.
....\src\CBT.NuGet.GlobalRestore\CBT\Module\After.Microsoft.Common.targets: RestoreGlobalNuGetPackages;$(RestoreDependsOn)
I have filed an issue.
|
|
||
| <PropertyGroup Condition=" '$(CBTEnableAggregatePackageRestore)' == 'true' "> | ||
| <CBTNuGetAggregatePackagePropertyFile Condition=" '$(CBTNuGetAggregatePackagePropertyFile)' == '' ">$(IntermediateOutputPath)\AggregatePackages.props</CBTNuGetAggregatePackagePropertyFile> | ||
| <CBTNuGetAggregatePackagePropertiesCreated>$(CBTNuGetTasksAssemblyPath.GetType().Assembly.GetType('System.AppDomain').GetProperty('CurrentDomain').GetValue(null).CreateInstanceFromAndUnwrap($(CBTNuGetTasksAssemblyPath), 'CBT.NuGet.Tasks.AggregatePackages').Execute('$(CBTAggregateDestPackageRoot)', '$(CBTAggregatePackage)', '$(CBTNuGetAggregatePackagePropertyFile)'))</CBTNuGetAggregatePackagePropertiesCreated> |
There was a problem hiding this comment.
We'll need to figure out how to make this work in Visual Studio...
There was a problem hiding this comment.
It would be neat if we could come up with a generic pattern or solution for these hacks something where it would do a popup in vs saying doing custom cbt action'somecustomerrormessageforaction'.
| Remove, | ||
| } | ||
|
|
||
| public AggregatePackage(string outPropertyId, List<KeyValuePair<AggregateOption, string>> packagesToAggregate, string destinationRoot) |
There was a problem hiding this comment.
Is packagesToAggregate a List<T> because it can contain duplicates or can it be an IDictionary<TKey, TValue>?
There was a problem hiding this comment.
I was just creating an ordered list of tuple pairs with <Action, Folder> action being add or remove from aggregate and folder being the folder that should be added to the aggregate or have it's contents removed from the aggregate.
Instead of using Tuple I just used a KVP which technically isn't correct. If I were to make the folder path a key then that could be unique but I didn't feel the need to remove duplicates as I didn't want to have to reason about which was the right one to remove. And I didn't feel the need to throw an error on a duplicate entry as it would be a harmless authoring error that could be intentional, (though wasted time) due to prior constraints of the project.
Anyway let me know if you have a better object structure in mind.
| { | ||
| OutPropertyId = outPropertyId; | ||
| PackagesToAggregate = packagesToAggregate; | ||
| OutPropertyValue = Path.Combine(destinationRoot, string.Format("{0}.{1}", outPropertyId, GetOutputPropertyHash())); |
There was a problem hiding this comment.
No more String.Format(), long live string interpolation
OutPropertyValue = Path.Combine(destinationRoot, $"{outPropertyId}.{GetOutputPropertyHash()}");
| } | ||
| } | ||
|
|
||
| public bool CreateAggregatePackage() |
There was a problem hiding this comment.
This method needs some logging.
There was a problem hiding this comment.
Yea I don't disagree with that. Is there a clean pattern you can think of to do logging with out having to make this AggregatePackage object aware of the msbuild logger by passing it in. Perhaps in this case that is a little over engineering.
There was a problem hiding this comment.
We should probably just move this method to the AggregatePackages task and leave the AggregatePackage class as just a data structure.
|
|
||
| public override bool Execute() | ||
| { | ||
| BuildEngine = new CBTBuildEngine(); |
There was a problem hiding this comment.
You shouldn't need to create a new CBTBuildEngine in this case since this code is running when the task is running as a real task. Set this on line 91 instead
There was a problem hiding this comment.
not sure why I had that there. Thanks
| BuildEngine = new CBTBuildEngine(); | ||
| Dictionary<string, string> propertiesToCreate = new Dictionary<string, string>(); | ||
|
|
||
| foreach (var aggPkg in PackagesToAggregate.Split(';')) |
There was a problem hiding this comment.
Be sure to ignore empty entries and trim here
There was a problem hiding this comment.
the string is nullorwhitespace should handle the null,empty,and whitespace scenario all of which I am not interested in. However I have added a splitoptions to remoteemptyentries as well.
| { | ||
| continue; | ||
| } | ||
| var pkg = AggregatePackage.ParseIntoPackage(aggPkg, AggregateDestRoot); |
There was a problem hiding this comment.
I like the TryParse pattern since its a little more standard. What do you think?
There was a problem hiding this comment.
standards are good. ;)
| catch (DirectoryNotFoundException exp) | ||
| { | ||
| Log.LogError(exp.Message); | ||
| throw; |
There was a problem hiding this comment.
This should probably not throw right?
… and detect content changes.
| // We don't care about directories in aggregation since removing directories is not possible given current implementation. | ||
| // This is in the scenario where the user aggregates against a nuget package and a checked in folder under source control that contains a config file or whatever. | ||
| // Concern on aggregating very large folders as this is disk IO. | ||
| foreach (var f in Directory.GetFiles(pkg.Folder, "*.*", SearchOption.AllDirectories)) |
There was a problem hiding this comment.
Perhaps instead of by default computing the last write time on the files being aggregated we put this behind a flag that is set to off unless they enable it to true or we detect a folder to aggregate is not under the package root.
Because imagine if every project had to parse over the msbuild.corext package to compute it's hash folder.
The scenario I am getting at is to avoid incorrect hard to debug build errors.
There was a problem hiding this comment.
At the very least we should use EnumerateFiles instead of GetFiles. I definitely would like to find a more efficient way of hashing the aggregated packages. Could it just be a hash of the package names?
There was a problem hiding this comment.
Actually I have an idea. I will send an update. Basically we only care about non immutable folders. So we can pass a list of immutable roots and that can be a ; delimited list of paths and by default that can be the nuget root. This would resolve the issue in the 99% case. In the 1% case the user has the escape hatch to say their immutable folder is really a non-immutable one.
|
I believe I have addressed your comments. There is one perf concern I have out to you for input. So please review again and take a look. |
| // We don't care about directories in aggregation since removing directories is not possible given current implementation. | ||
| // This is in the scenario where the user aggregates against a nuget package and a checked in folder under source control that contains a config file or whatever. | ||
| // Concern on aggregating very large folders as this is disk IO. | ||
| foreach (var f in Directory.GetFiles(pkg.Folder, "*.*", SearchOption.AllDirectories)) |
There was a problem hiding this comment.
At the very least we should use EnumerateFiles instead of GetFiles. I definitely would like to find a more efficient way of hashing the aggregated packages. Could it just be a hash of the package names?
| throw new DirectoryNotFoundException("Source Directory does not exist or could not be found: " + sourceDirName); | ||
| } | ||
| DirectoryInfo dir = new DirectoryInfo(sourceDirName); | ||
| DirectoryInfo[] dirs = dir.GetDirectories(); |
There was a problem hiding this comment.
Use EnumerateDirectories instead and EnumerateFiles instead of GetFiles below. We should probably revisit this method and see if Async copies or parallelism would help here.
| project.Save(propsFile); | ||
| } | ||
|
|
||
| internal IEnumerable<AggregatePackage> ParsePackagesToAggregate() |
There was a problem hiding this comment.
You should include unit tests with this pull request or open an issue to write some for this sprint.
Create a basic aggregate package option where the user can set a property like.
<CBTAggregatePackage>AAG_TEST=d:\tmp\aag\src*d:\tmp\aag\src2|d:\tmp\aag\rem1</CBTAggregatePackage>
And it will use copy d:\tmp\agg\src then overlay d:\tmp\aag\src2 then remove from that overlay any files with names that match in d:\tmp\aag\rem1.
Pay attention to the comments there are many issues we need to consider with this approach.
To address Issue #60