diff --git a/.fantomasignore b/.fantomasignore index f974955bb889e4dfb9e8b6d2efdcf0d4c4cc7c45..cc41976ea8d28567b1a107da0a6e8b38747f0c18 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -16,7 +16,6 @@ src/FSharp.Core/**/*.fs src/Compiler/**/*.fs src/fsc/**/*.fs src/fscAnyCpu/**/*.fs -src/FSharp.Build/**/*.fs src/FSharp.Compiler.Server.Shared/**/*.fs src/FSharp.DependencyManager.Nuget/**/*.fs src/fsi/**/*.fs diff --git a/src/FSharp.Build/CreateFSharpManifestResourceName.fs b/src/FSharp.Build/CreateFSharpManifestResourceName.fs index b377442c62baf2eed25a95cc2eed0ab1c3f376c2..175a76d203007e511451de199717b34c43728b5b 100644 --- a/src/FSharp.Build/CreateFSharpManifestResourceName.fs +++ b/src/FSharp.Build/CreateFSharpManifestResourceName.fs @@ -4,9 +4,7 @@ namespace FSharp.Build open System open System.IO -open System.Text open Microsoft.Build.Tasks -open Microsoft.Build.Utilities type CreateFSharpManifestResourceName public () = inherit CreateCSharpManifestResourceName() @@ -14,12 +12,14 @@ type CreateFSharpManifestResourceName public () = // When set to true, generate resource names in the same way as C# with root namespace and folder names member val UseStandardResourceNames = false with get, set - override this.CreateManifestName - ((fileName:string), - (linkFileName:string), - (rootNamespace:string), (* may be null *) - (dependentUponFileName:string), (* may be null *) - (binaryStream:System.IO.Stream) (* may be null *)) : string = + override this.CreateManifestName + ( + fileName: string, + linkFileName: string, + rootNamespace: string, // may be null + dependentUponFileName: string, // may be null + binaryStream: Stream // may be null + ) : string = // The Visual CSharp and XBuild CSharp toolchains transform resource names like this: // SubDir\abc.resx --> SubDir.abc.resources @@ -30,37 +30,56 @@ type CreateFSharpManifestResourceName public () = let fileName, linkFileName, rootNamespace = match this.UseStandardResourceNames with - | true -> - fileName, linkFileName, rootNamespace + | true -> fileName, linkFileName, rootNamespace | false -> - let runningOnMono = + let runningOnMono = try System.Type.GetType("Mono.Runtime") <> null - with e -> - false - let fileName = if not runningOnMono || fileName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase) then fileName else Path.GetFileName(fileName) - let linkFileName = if not runningOnMono || linkFileName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase) then linkFileName else Path.GetFileName(linkFileName) - fileName, linkFileName, "" + with + | e -> false - let embeddedFileName = + let fileName = + if + not runningOnMono + || fileName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase) + then + fileName + else + Path.GetFileName(fileName) + + let linkFileName = + if + not runningOnMono + || linkFileName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase) + then + linkFileName + else + Path.GetFileName(linkFileName) + + fileName, linkFileName, "" + + let embeddedFileName = match linkFileName with - | null -> fileName - | _ -> linkFileName + | null -> fileName + | _ -> linkFileName - // since we do not support resources dependent on a form, we always pass null for a binary stream - let cSharpResult = + // since we do not support resources dependent on a form, we always pass null for a binary stream + let cSharpResult = base.CreateManifestName(fileName, linkFileName, rootNamespace, dependentUponFileName, null) // Workaround that makes us keep .resources extension on both 3.5 and 3.5SP1 // 3.5 stripped ".resources", 3.5 SP1 does not. We should do 3.5SP1 thing let extensionToWorkaround = ".resources" - if embeddedFileName.EndsWith(extensionToWorkaround, StringComparison.OrdinalIgnoreCase) - && not (cSharpResult.EndsWith(extensionToWorkaround, StringComparison.OrdinalIgnoreCase)) then + + if + embeddedFileName.EndsWith(extensionToWorkaround, StringComparison.OrdinalIgnoreCase) + && not (cSharpResult.EndsWith(extensionToWorkaround, StringComparison.OrdinalIgnoreCase)) + then cSharpResult + extensionToWorkaround else cSharpResult - - - override _.IsSourceFile (fileName: string) = + + override _.IsSourceFile(fileName: string) = let extension = Path.GetExtension(fileName) - (String.Equals(extension, ".fs", StringComparison.OrdinalIgnoreCase) || - String.Equals(extension, ".ml", StringComparison.OrdinalIgnoreCase)) \ No newline at end of file + + (String.Equals(extension, ".fs", StringComparison.OrdinalIgnoreCase) + || String.Equals(extension, ".ml", StringComparison.OrdinalIgnoreCase)) diff --git a/src/FSharp.Build/FSharpCommandLineBuilder.fs b/src/FSharp.Build/FSharpCommandLineBuilder.fs index 2fd7dfa1d06409e70c670e85859454561f58c486..bf86d185edc2e4aa22a1dc172bd219c85b48201d 100644 --- a/src/FSharp.Build/FSharpCommandLineBuilder.fs +++ b/src/FSharp.Build/FSharpCommandLineBuilder.fs @@ -10,20 +10,23 @@ open Internal.Utilities [] [] -do() +do () // Shim to match nullness checking library support in preview [] -module Utils = +module Utils = /// Match on the nullness of an argument. - let inline (|Null|NonNull|) (x: 'T) : Choice = match x with null -> Null | v -> NonNull v + let inline (|Null|NonNull|) (x: 'T) : Choice = + match x with + | null -> Null + | v -> NonNull v /// Indicates that a type may be null. 'MaybeNull' used internally in the F# compiler as unchecked /// replacement for 'string?' for example for future FS-1060. - type 'T MaybeNull when 'T : null and 'T: not struct = 'T + type 'T MaybeNull when 'T: null and 'T: not struct = 'T -type FSharpCommandLineBuilder () = +type FSharpCommandLineBuilder() = // In addition to generating a command-line that will be handed to cmd.exe, we also generate // an array of individual arguments. The former needs to be quoted (and cmd.exe will strip the @@ -31,8 +34,8 @@ type FSharpCommandLineBuilder () = // class gets us out of the business of unparsing-then-reparsing arguments. let builder = new CommandLineBuilder() - let mutable args = [] // in reverse order - let mutable srcs = [] // in reverse order + let mutable args = [] // in reverse order + let mutable srcs = [] // in reverse order /// Return a list of the arguments (with no quoting for the cmd.exe shell) member _.CapturedArguments() = List.rev args @@ -48,8 +51,9 @@ type FSharpCommandLineBuilder () = // do not update "args", not used for item in filenames do let tmp = new CommandLineBuilder() - tmp.AppendSwitchUnquotedIfNotNull("", item.ItemSpec) // we don't want to quote the file name, this is a way to get that + tmp.AppendSwitchUnquotedIfNotNull("", item.ItemSpec) // we don't want to quote the file name, this is a way to get that let s = tmp.ToString() + if s <> String.Empty then srcs <- tmp.ToString() :: srcs @@ -58,6 +62,7 @@ type FSharpCommandLineBuilder () = let tmp = new CommandLineBuilder() tmp.AppendSwitchUnquotedIfNotNull(switch, values, sep) let s = tmp.ToString() + if s <> String.Empty then args <- s :: args @@ -66,43 +71,57 @@ type FSharpCommandLineBuilder () = builder.AppendSwitchIfNotNull(switch, value) let tmp = new CommandLineBuilder() tmp.AppendSwitchUnquotedIfNotNull(switch, value) + let providedMetaData = - metadataNames - |> Array.filter (String.IsNullOrWhiteSpace >> not) + metadataNames |> Array.filter (String.IsNullOrWhiteSpace >> not) + if providedMetaData.Length > 0 then tmp.AppendTextUnquoted "," - tmp.AppendTextUnquoted (providedMetaData|> String.concat ",") + tmp.AppendTextUnquoted(providedMetaData |> String.concat ",") + let s = tmp.ToString() + if s <> String.Empty then args <- s :: args member _.AppendSwitchUnquotedIfNotNull(switch: string, value: string MaybeNull) = - assert(switch = "") // we only call this method for "OtherFlags" + assert (switch = "") // we only call this method for "OtherFlags" // Unfortunately we still need to mimic what cmd.exe does, but only for "OtherFlags". - let ParseCommandLineArgs(commandLine: string) = // returns list in reverse order + let ParseCommandLineArgs (commandLine: string) = // returns list in reverse order let mutable args = [] let mutable i = 0 // index into commandLine let len = commandLine.Length + while i < len do // skip whitespace while i < len && System.Char.IsWhiteSpace(commandLine, i) do i <- i + 1 + if i < len then // parse an argument let sb = new StringBuilder() let mutable finished = false let mutable insideQuote = false + while i < len && not finished do match commandLine.[i] with - | '"' -> insideQuote <- not insideQuote; i <- i + 1 + | '"' -> + insideQuote <- not insideQuote + i <- i + 1 | c when not insideQuote && System.Char.IsWhiteSpace(c) -> finished <- true - | c -> sb.Append(c) |> ignore; i <- i + 1 + | c -> + sb.Append(c) |> ignore + i <- i + 1 + args <- sb.ToString() :: args + args + builder.AppendSwitchUnquotedIfNotNull(switch, value) let tmp = new CommandLineBuilder() tmp.AppendSwitchUnquotedIfNotNull(switch, value) let s = tmp.ToString() + if s <> String.Empty then args <- ParseCommandLineArgs(s) @ args @@ -110,8 +129,5 @@ type FSharpCommandLineBuilder () = builder.AppendSwitch(switch) args <- switch :: args - member internal x.GetCapturedArguments() = - [| - yield! x.CapturedArguments() - yield! x.CapturedFilenames() - |] + member internal x.GetCapturedArguments() = + [| yield! x.CapturedArguments(); yield! x.CapturedFilenames() |] diff --git a/src/FSharp.Build/FSharpEmbedResXSource.fs b/src/FSharp.Build/FSharpEmbedResXSource.fs index fc8b9167d38f5442b75a683addf531d1f8b671c2..24665c44f5881d684fa9217355aab94ebb6aa98c 100644 --- a/src/FSharp.Build/FSharpEmbedResXSource.fs +++ b/src/FSharp.Build/FSharpEmbedResXSource.fs @@ -3,8 +3,6 @@ namespace FSharp.Build open System -open System.Collections -open System.Globalization open System.IO open System.Linq open System.Text @@ -20,7 +18,8 @@ type FSharpEmbedResXSource() = let mutable _outputPath: string = "" let mutable _targetFramework: string = "" - let boilerplate = @"// + let boilerplate = + @"// namespace {0} @@ -32,95 +31,128 @@ module internal {1} = let ResourceManager = new System.Resources.ResourceManager(""{2}"", C(0).GetType().GetTypeInfo().Assembly) let GetString(name:System.String) : System.String = ResourceManager.GetString(name, Culture)" - let boilerplateGetObject = " let GetObject(name:System.String) : System.Object = ResourceManager.GetObject(name, Culture)" + let boilerplateGetObject = + " let GetObject(name:System.String) : System.Object = ResourceManager.GetObject(name, Culture)" - let generateSource (resx:string) (fullModuleName:string) (generateLegacy:bool) (generateLiteral:bool) = + let generateSource (resx: string) (fullModuleName: string) (generateLegacy: bool) (generateLiteral: bool) = try let printMessage = printfn "FSharpEmbedResXSource: %s" let justFileName = Path.GetFileNameWithoutExtension(resx) let sourcePath = Path.Combine(_outputPath, justFileName + ".fs") // simple up-to-date check - if File.Exists(resx) && File.Exists(sourcePath) && - File.GetLastWriteTimeUtc(resx) <= File.GetLastWriteTimeUtc(sourcePath) then + if File.Exists(resx) + && File.Exists(sourcePath) + && File.GetLastWriteTimeUtc(resx) <= File.GetLastWriteTimeUtc(sourcePath) then printMessage (sprintf "Skipping generation: '%s' since it is up-to-date." sourcePath) Some(sourcePath) else let namespaceName, moduleName = let parts = fullModuleName.Split('.') - if parts.Length = 1 then ("global", parts.[0]) - else (String.Join(".", parts, 0, parts.Length - 1), parts.[parts.Length - 1]) - let generateGetObject = not (_targetFramework.StartsWith("netstandard1.") || _targetFramework.StartsWith("netcoreapp1.")) + + if parts.Length = 1 then + ("global", parts.[0]) + else + (String.Join(".", parts, 0, parts.Length - 1), parts.[parts.Length - 1]) + + let generateGetObject = + not ( + _targetFramework.StartsWith("netstandard1.") + || _targetFramework.StartsWith("netcoreapp1.") + ) + printMessage (sprintf "Generating code for target framework %s" _targetFramework) - let sb = StringBuilder().AppendLine(String.Format(boilerplate, namespaceName, moduleName, justFileName)) - if generateGetObject then sb.AppendLine(boilerplateGetObject) |> ignore + + let sb = + StringBuilder() + .AppendLine(String.Format(boilerplate, namespaceName, moduleName, justFileName)) + + if generateGetObject then + sb.AppendLine(boilerplateGetObject) |> ignore + printMessage <| sprintf "Generating: %s" sourcePath + let body = let xname = XName.op_Implicit + XDocument.Load(resx).Descendants(xname "data") - |> Seq.fold (fun (sb:StringBuilder) (node:XElement) -> - let name = - match node.Attribute(xname "name") with - | null -> failwith (sprintf "Missing resource name on element '%s'" (node.ToString())) - | attr -> attr.Value - let docComment = - match node.Elements(xname "value").FirstOrDefault() with - | null -> failwith <| sprintf "Missing resource value for '%s'" name - | element -> element.Value.Trim() - let identifier = if Char.IsLetter(name.[0]) || name.[0] = '_' then name else "_" + name - let commentBody = - XElement(xname "summary", docComment).ToString().Split([|"\r\n"; "\r"; "\n"|], StringSplitOptions.None) - |> Array.fold (fun (sb:StringBuilder) line -> sb.AppendLine(" /// " + line)) (StringBuilder()) - // add the resource - let accessorBody = - match (generateLegacy, generateLiteral) with - | (true, true) -> sprintf " []\n let %s = \"%s\"" identifier name - | (true, false) -> sprintf " let %s = \"%s\"" identifier name // the [] attribute can't be used for FSharp.Core - | (false, _) -> - let isStringResource = node.Attribute(xname "type") |> isNull - match (isStringResource, generateGetObject) with - | (true, _) -> sprintf " let %s() = GetString(\"%s\")" identifier name - | (false, true) -> sprintf " let %s() = GetObject(\"%s\")" identifier name - | (false, false) -> "" // the target runtime doesn't support non-string resources - // TODO: When calling the `GetObject` version, parse the `type` attribute to discover the proper return type - sb.AppendLine().Append(commentBody).AppendLine(accessorBody) - ) sb + |> Seq.fold + (fun (sb: StringBuilder) (node: XElement) -> + let name = + match node.Attribute(xname "name") with + | null -> failwith (sprintf "Missing resource name on element '%s'" (node.ToString())) + | attr -> attr.Value + + let docComment = + match node.Elements(xname "value").FirstOrDefault() with + | null -> failwith <| sprintf "Missing resource value for '%s'" name + | element -> element.Value.Trim() + + let identifier = + if Char.IsLetter(name.[0]) || name.[0] = '_' then + name + else + "_" + name + + let commentBody = + XElement(xname "summary", docComment) + .ToString() + .Split([| "\r\n"; "\r"; "\n" |], StringSplitOptions.None) + |> Array.fold + (fun (sb: StringBuilder) line -> sb.AppendLine(" /// " + line)) + (StringBuilder()) + // add the resource + let accessorBody = + match (generateLegacy, generateLiteral) with + | (true, true) -> sprintf " []\n let %s = \"%s\"" identifier name + | (true, false) -> sprintf " let %s = \"%s\"" identifier name // the [] attribute can't be used for FSharp.Core + | (false, _) -> + let isStringResource = node.Attribute(xname "type") |> isNull + + match (isStringResource, generateGetObject) with + | (true, _) -> sprintf " let %s() = GetString(\"%s\")" identifier name + | (false, true) -> sprintf " let %s() = GetObject(\"%s\")" identifier name + | (false, false) -> "" // the target runtime doesn't support non-string resources + // TODO: When calling the `GetObject` version, parse the `type` attribute to discover the proper return type + sb.AppendLine().Append(commentBody).AppendLine(accessorBody)) + sb + File.WriteAllText(sourcePath, body.ToString()) printMessage <| sprintf "Done: %s" sourcePath Some(sourcePath) - with e -> + with + | e -> printf "An exception occurred when processing '%s'\n%s" resx (e.ToString()) None [] member _.EmbeddedResource - with get() = _embeddedText - and set(value) = _embeddedText <- value + with get () = _embeddedText + and set (value) = _embeddedText <- value [] member _.IntermediateOutputPath - with get() = _outputPath - and set(value) = _outputPath <- value + with get () = _outputPath + and set (value) = _outputPath <- value member _.TargetFramework - with get() = _targetFramework - and set(value) = _targetFramework <- value + with get () = _targetFramework + and set (value) = _targetFramework <- value [] - member _.GeneratedSource - with get() = _generatedSource + member _.GeneratedSource = _generatedSource interface ITask with member _.BuildEngine - with get() = _buildEngine - and set(value) = _buildEngine <- value + with get () = _buildEngine + and set (value) = _buildEngine <- value member _.HostObject - with get() = _hostObject - and set(value) = _hostObject <- value + with get () = _hostObject + and set (value) = _hostObject <- value member this.Execute() = - let getBooleanMetadata (metadataName:string) (defaultValue:bool) (item:ITaskItem) = + let getBooleanMetadata (metadataName: string) (defaultValue: bool) (item: ITaskItem) = match item.GetMetadata(metadataName) with | value when String.IsNullOrWhiteSpace(value) -> defaultValue | value -> @@ -128,19 +160,26 @@ module internal {1} = | "true" -> true | "false" -> false | _ -> failwith (sprintf "Expected boolean value for '%s' found '%s'" metadataName value) + let mutable success = true + let generatedSource = - [| for item in this.EmbeddedResource do - if getBooleanMetadata "GenerateSource" false item then - let moduleName = - match item.GetMetadata("GeneratedModuleName") with - | null | "" -> Path.GetFileNameWithoutExtension(item.ItemSpec) - | value -> value - let generateLegacy = getBooleanMetadata "GenerateLegacyCode" false item - let generateLiteral = getBooleanMetadata "GenerateLiterals" true item - match generateSource item.ItemSpec moduleName generateLegacy generateLiteral with - | Some (source) -> yield TaskItem(source) :> ITaskItem - | None -> success <- false + [| + for item in this.EmbeddedResource do + if getBooleanMetadata "GenerateSource" false item then + let moduleName = + match item.GetMetadata("GeneratedModuleName") with + | null + | "" -> Path.GetFileNameWithoutExtension(item.ItemSpec) + | value -> value + + let generateLegacy = getBooleanMetadata "GenerateLegacyCode" false item + let generateLiteral = getBooleanMetadata "GenerateLiterals" true item + + match generateSource item.ItemSpec moduleName generateLegacy generateLiteral with + | Some (source) -> yield TaskItem(source) :> ITaskItem + | None -> success <- false |] + _generatedSource <- generatedSource success diff --git a/src/FSharp.Build/FSharpEmbedResourceText.fs b/src/FSharp.Build/FSharpEmbedResourceText.fs index f43fef77a43c335e331ab1299ddf9881d7879a0d..eb1907187a872a5352ebbe9c821d551d80e42304 100644 --- a/src/FSharp.Build/FSharpEmbedResourceText.fs +++ b/src/FSharp.Build/FSharpEmbedResourceText.fs @@ -14,10 +14,10 @@ type FSharpEmbedResourceText() = let mutable _generatedResx: ITaskItem[] = [||] let mutable _outputPath: string = "" - let PrintErr(fileName, line, msg) = + let PrintErr (fileName, line, msg) = printfn "%s(%d): error : %s" fileName line msg - let Err(fileName, line, msg) = + let Err (fileName, line, msg) = PrintErr(fileName, line, msg) printfn "Note that the syntax of each line is one of these three alternatives:" printfn "# comment" @@ -25,7 +25,8 @@ type FSharpEmbedResourceText() = printfn "errNum,ident,\"string\"" failwith (sprintf "there were errors in the file '%s'" fileName) - let xmlBoilerPlateString = @" + let xmlBoilerPlateString = + @"