Skip to content

Commit b63e370

Browse files
committed
Merge pull request #882 from devboy/feature/exclusion-syntax-template-files
Exclusion syntax for paket.template files
2 parents 83fd6fd + 9b5c712 commit b63e370

4 files changed

Lines changed: 208 additions & 69 deletions

File tree

‎docs/content/template-files.md‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ If the source part refers to a file then it is copied into the target directory.
100100
refers to a directory, the contents of the directory will be copied into the target folder.
101101
If you omit the target folder, then the source is copied into the `lib` folder of the package.
102102

103+
Excluding certain files looks like this:
104+
105+
[lang=batchfile]
106+
files
107+
relative/to/template/file ==> folder/in/nupkg
108+
second/thing/**/file.* ==> folder/in/nupkg
109+
!second/thing/**/file.zip
110+
../outside/file.* ==> folder/in/nupkg/other
111+
!../outside/file.zip
112+
113+
The pattern needs to match file-names, excluding directories like `!second` won't have an effect, please use `!second/*.*` instead.
114+
103115
In a project template, the files included will be:
104116

105117
* the output assembly of the matching project (in the correct lib directory if a library, or tools if an exe)
@@ -123,7 +135,7 @@ A block with framework assembly references looks like this:
123135
[lang=batchfile]
124136
frameworkAssemblies
125137
System.Xml
126-
System.Xml.Linq
138+
System.Xml.Linq
127139

128140
If you omit the references block then all libraries in the packages will get referenced.
129141

@@ -147,4 +159,4 @@ It's possible to use `CURRENTVERSION` as a placeholder for the current version o
147159
In a project file, the following dependencies will be added:
148160

149161
* any paket dependency with the range specified in the [`paket.dependencies` file](dependencies-file.html).
150-
* any project reference with a matching paket.template file with a minimum version requirement of the version currently being packaged.
162+
* any project reference with a matching paket.template file with a minimum version requirement of the version currently being packaged.

‎src/Paket.Core/NupkgWriter.fs‎

Lines changed: 97 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,105 +6,106 @@ open System.Xml.Linq
66
open System.IO.Compression
77
open Paket
88
open System.Text
9+
open System.Text.RegularExpressions
910
open System.Xml
1011

1112
let nuspecId = "nuspec"
1213
let corePropsId = "coreProp"
1314
let contentTypePath = "[Content_Types].xml"
1415

15-
let contentTypeDoc fileList =
16+
let contentTypeDoc fileList =
1617
let declaration = XDeclaration("1.0", "UTF-8", "yes")
1718
let ns = XNamespace.Get "http://schemas.openxmlformats.org/package/2006/content-types"
1819
let root = XElement(ns + "Types")
19-
20+
2021
let defaultNode extension contentType =
2122
let def = XElement(ns + "Default")
2223
def.SetAttributeValue(XName.Get "Extension", extension)
2324
def.SetAttributeValue(XName.Get "ContentType", contentType)
2425
def
25-
26-
let knownExtensions =
26+
27+
let knownExtensions =
2728
Map.ofList [ "rels", "application/vnd.openxmlformats-package.relationships+xml"
2829
"psmdcp", "application/vnd.openxmlformats-package.core-properties+xml" ]
29-
30+
3031
let ext path = Path.GetExtension(path).TrimStart([| '.' |]).ToLowerInvariant()
31-
32-
let fType ext =
32+
33+
let fType ext =
3334
knownExtensions
3435
|> Map.tryFind ext
35-
|> function
36+
|> function
3637
| Some ft -> ft
3738
| None -> "application/octet"
38-
39-
let contentTypes =
39+
40+
let contentTypes =
4041
fileList
41-
|> Seq.choose (fun f ->
42+
|> Seq.choose (fun f ->
4243
let e = ext f
43-
if String.IsNullOrWhiteSpace e then
44+
if String.IsNullOrWhiteSpace e then
4445
None
4546
else Some(e, fType e))
4647
|> Seq.distinct
4748
|> Seq.iter (fun (ex, ct) -> defaultNode ex ct |> root.Add)
48-
49+
4950
XDocument(declaration, box root)
5051

51-
let nuspecDoc (info:CompleteInfo) =
52+
let nuspecDoc (info:CompleteInfo) =
5253
let core,optional = info
5354
let declaration = XDeclaration("1.0", "UTF-8", "yes")
5455
let ns = XNamespace.Get "http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd"
5556
let root = XElement(ns + "package")
56-
57-
let addChildNode (parent : XElement) name value =
57+
58+
let addChildNode (parent : XElement) name value =
5859
let node = XElement(ns + name)
5960
node.SetValue value
6061
parent.Add node
61-
62+
6263
let metadataNode = XElement(ns + "metadata")
6364
root.Add metadataNode
6465
let (!!) = addChildNode metadataNode
65-
66-
let (!!?) nodeName strOpt =
66+
67+
let (!!?) nodeName strOpt =
6768
match strOpt with
6869
| Some s -> addChildNode metadataNode nodeName s
6970
| None -> ()
70-
71-
let buildFrameworkReferencesNode libName =
71+
72+
let buildFrameworkReferencesNode libName =
7273
let element = XElement(ns + "frameworkAssembly")
7374
element.SetAttributeValue(XName.Get "assemblyName", libName)
7475
element
75-
76-
let buildFrameworkReferencesNode frameworkAssembliesList =
76+
77+
let buildFrameworkReferencesNode frameworkAssembliesList =
7778
if frameworkAssembliesList = [] then () else
7879
let d = XElement(ns + "frameworkAssemblies")
7980
frameworkAssembliesList |> List.iter (buildFrameworkReferencesNode >> d.Add)
8081
metadataNode.Add d
8182

82-
let buildDependencyNode (Id, (VersionRequirement(range, _))) =
83+
let buildDependencyNode (Id, (VersionRequirement(range, _))) =
8384
let dep = XElement(ns + "dependency")
8485
dep.SetAttributeValue(XName.Get "id", Id)
8586

8687
match range.FormatInNuGetSyntax() with
8788
| "0" -> ()
8889
| versionStr -> dep.SetAttributeValue(XName.Get "version", versionStr)
8990
dep
90-
91-
let buildDependenciesNode dependencyList =
91+
92+
let buildDependenciesNode dependencyList =
9293
if dependencyList = [] then () else
9394
let d = XElement(ns + "dependencies")
9495
dependencyList |> List.iter (buildDependencyNode >> d.Add)
9596
metadataNode.Add d
9697

97-
let buildReferenceNode (fileName) =
98+
let buildReferenceNode (fileName) =
9899
let dep = XElement(ns + "reference")
99100
dep.SetAttributeValue(XName.Get "file", fileName)
100101
dep
101102

102-
let buildReferencesNode referenceList =
103+
let buildReferencesNode referenceList =
103104
if referenceList = [] then () else
104105
let d = XElement(ns + "references")
105106
referenceList |> List.iter (buildReferenceNode >> d.Add)
106107
metadataNode.Add d
107-
108+
108109
!! "id" core.Id
109110
match core.Version with
110111
| Some v -> !! "version" <| v.ToString()
@@ -133,20 +134,20 @@ let nuspecDoc (info:CompleteInfo) =
133134

134135
let corePropsPath = sprintf "package/services/metadata/core-properties/%s.psmdcp" corePropsId
135136

136-
let corePropsDoc (core : CompleteCoreInfo) =
137+
let corePropsDoc (core : CompleteCoreInfo) =
137138
let declaration = XDeclaration("1.0", "UTF-8", "yes")
138139
let ns = XNamespace.Get "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"
139140
let dc = XNamespace.Get "http://purl.org/dc/elements/1.1/"
140141
let dcterms = XNamespace.Get "http://purl.org/dc/terms/"
141142
let xsi = XNamespace.Get "http://www.w3.org/2001/XMLSchema-instance"
142-
let root =
143+
let root =
143144
XElement
144-
(ns + "Relationships", XAttribute(XName.Get "xmlns", ns.NamespaceName),
145-
XAttribute(XNamespace.Xmlns + "dc", dc.NamespaceName),
146-
XAttribute(XNamespace.Xmlns + "dcterms", dcterms.NamespaceName),
145+
(ns + "Relationships", XAttribute(XName.Get "xmlns", ns.NamespaceName),
146+
XAttribute(XNamespace.Xmlns + "dc", dc.NamespaceName),
147+
XAttribute(XNamespace.Xmlns + "dcterms", dcterms.NamespaceName),
147148
XAttribute(XNamespace.Xmlns + "xsi", xsi.NamespaceName))
148-
149-
let (!!) (ns : XNamespace) name value =
149+
150+
let (!!) (ns : XNamespace) name value =
150151
let node = XElement(ns + name)
151152
node.SetValue value
152153
root.Add node
@@ -161,12 +162,12 @@ let corePropsDoc (core : CompleteCoreInfo) =
161162

162163
let relsPath = "_rels/.rels"
163164

164-
let relsDoc (core : CompleteCoreInfo) =
165+
let relsDoc (core : CompleteCoreInfo) =
165166
let declaration = XDeclaration("1.0", "UTF-8", "yes")
166167
let ns = XNamespace.Get "http://schemas.openxmlformats.org/package/2006/relationships"
167168
let root = XElement(ns + "Relationships")
168-
169-
let r type' target id' =
169+
170+
let r type' target id' =
170171
let rel = XElement(ns + "Relationship")
171172
rel.SetAttributeValue(XName.Get "Type", type')
172173
rel.SetAttributeValue(XName.Get "Target", target)
@@ -176,31 +177,60 @@ let relsDoc (core : CompleteCoreInfo) =
176177
r "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" ("/" + corePropsPath) corePropsId
177178
XDocument(declaration, box root)
178179

179-
let xDocWriter (xDoc : XDocument) (stream : System.IO.Stream) =
180+
let xDocWriter (xDoc : XDocument) (stream : System.IO.Stream) =
180181
let settings = new XmlWriterSettings(Indent = true, Encoding = Encoding.UTF8)
181182
use xmlWriter = XmlWriter.Create(stream, settings)
182183
xDoc.WriteTo xmlWriter
183184
xmlWriter.Flush()
184185

185-
let writeNupkg (core : CompleteCoreInfo) optional =
186+
let writeNupkg (core : CompleteCoreInfo) optional =
186187
[ core.NuspecFileName, nuspecDoc(core,optional) |> xDocWriter
187188
corePropsPath, corePropsDoc core |> xDocWriter
188189
relsPath, relsDoc core |> xDocWriter ]
189190

190-
let Write (core : CompleteCoreInfo) optional workingDir outputDir =
191+
let Write (core : CompleteCoreInfo) optional workingDir outputDir =
191192
let outputPath = Path.Combine(outputDir, core.PackageFileName)
192193
if File.Exists outputPath then
193194
File.Delete outputPath
194195

195196
use zipToCreate = new FileStream(outputPath, FileMode.Create)
196197
use zipFile = new ZipArchive(zipToCreate,ZipArchiveMode.Create)
197198

198-
let entries = System.Collections.Generic.List<_>()
199-
200-
let addEntry path writerF =
199+
let entries = System.Collections.Generic.List<_>()
200+
201+
let fixRelativePath (p:string) =
202+
let isWinDrive = Regex(@"^\w:\\.*", RegexOptions.Compiled).IsMatch
203+
let isNixRoot = Regex(@"^\/.*", RegexOptions.Compiled).IsMatch
204+
205+
let prepend,path =
206+
match p with
207+
| s when isWinDrive s -> [|s.Substring(0,3)|],s.Substring(3)
208+
| s when isNixRoot s -> [|"/"|],s.Substring(1)
209+
| s when String.IsNullOrWhiteSpace s -> failwith "Empty exclusion path!"
210+
| s -> [||],s
211+
212+
path.Split('\\','/')
213+
|> Array.fold (fun (xs:string []) x ->
214+
match x with
215+
| s when "..".Equals s -> Array.sub xs 0 (xs.Length-1)
216+
| s when ".".Equals s -> xs
217+
| _ -> Array.append xs [|x|]) [||]
218+
|> Array.append prepend
219+
|> Array.fold (fun p' x -> Path.Combine(p',x)) ""
220+
221+
let exclusions =
222+
optional.FilesExcluded
223+
|> List.map (fun e -> Path.Combine(workingDir,e) |> fixRelativePath)
224+
|> List.map Fake.Globbing.isMatch
225+
226+
let isExcluded p =
227+
let path = DirectoryInfo(p).FullName
228+
exclusions |> List.exists (fun f -> f path)
229+
230+
let addEntry path writerF =
201231
if entries.Contains(path) then () else
202232
entries.Add path |> ignore
203-
let entry = zipFile.CreateEntry(path)
233+
let entry = zipFile.CreateEntry(path)
204234
use stream = entry.Open()
205235
writerF stream
206236
stream.Close()
@@ -211,7 +241,7 @@ let Write (core : CompleteCoreInfo) optional workingDir outputDir =
211241
zipFile.CreateEntryFromFile(source,path) |> ignore
212242

213243
let ensureValidTargetName (target:string) =
214-
let target =
244+
let target =
215245
target.Replace(" ", "%20").Replace("\\", "/")
216246
|> fun s -> if s.StartsWith("./") then s.Remove(0,2) else s
217247

@@ -222,35 +252,39 @@ let Write (core : CompleteCoreInfo) optional workingDir outputDir =
222252
| t -> t + "/"
223253

224254
// adds all files in a directory to the zipFile
225-
let rec addDir source target =
226-
let target = ensureValidTargetName target
227-
for file in Directory.EnumerateFiles(source,"*.*",SearchOption.TopDirectoryOnly) do
228-
let fi = FileInfo file
229-
let path = Path.Combine(target,fi.Name)
230-
addEntryFromFile path fi.FullName
255+
let rec addDir source target =
256+
if not <| isExcluded source then
257+
let target = ensureValidTargetName target
258+
for file in Directory.EnumerateFiles(source,"*.*",SearchOption.TopDirectoryOnly) do
259+
if not <| isExcluded file then
260+
let fi = FileInfo file
261+
let path = Path.Combine(target,fi.Name)
231262

232-
for dir in Directory.EnumerateDirectories(source,"*",SearchOption.TopDirectoryOnly) do
233-
let di = DirectoryInfo dir
234-
addDir di.FullName (Path.Combine(target,di.Name))
263+
addEntryFromFile path fi.FullName
264+
265+
for dir in Directory.EnumerateDirectories(source,"*",SearchOption.TopDirectoryOnly) do
266+
let di = DirectoryInfo dir
267+
addDir di.FullName (Path.Combine(target,di.Name))
235268

236269
// add files
237270
for fileName,targetFileName in optional.Files do
238271
let targetFileName = ensureValidTargetName targetFileName
239272
let source = Path.Combine(workingDir, fileName)
240273
if Directory.Exists source then
241274
addDir source targetFileName
242-
else
275+
else
243276
if File.Exists source then
244-
let fi = FileInfo(source)
245-
let path = Path.Combine(targetFileName,fi.Name)
246-
addEntryFromFile path source
247-
else
277+
if not <| isExcluded source then
278+
let fi = FileInfo(source)
279+
let path = Path.Combine(targetFileName,fi.Name)
280+
addEntryFromFile path source
281+
else
248282
failwithf "Could not find source file %s" source
249283

250284
// add metadata
251-
for path, writer in writeNupkg core optional do
285+
for path, writer in writeNupkg core optional do
252286
addEntry path writer
253-
287+
254288
entries
255289
|> Seq.toList
256290
|> contentTypeDoc

0 commit comments

Comments
 (0)