diff --git a/src/Fable.Cli/Fable.Cli.fsproj b/src/Fable.Cli/Fable.Cli.fsproj index b335bb23d4..dc3877559f 100644 --- a/src/Fable.Cli/Fable.Cli.fsproj +++ b/src/Fable.Cli/Fable.Cli.fsproj @@ -17,6 +17,7 @@ fable true F# to JS compiler + $(OtherFlags) --nowarn:3536 @@ -36,6 +37,7 @@ + @@ -44,7 +46,7 @@ - + diff --git a/src/Fable.Cli/ProjectCracker.fs b/src/Fable.Cli/ProjectCracker.fs index cf414b512a..9f74a2ea35 100644 --- a/src/Fable.Cli/ProjectCracker.fs +++ b/src/Fable.Cli/ProjectCracker.fs @@ -12,6 +12,8 @@ open Fable.AST open Globbing.Operators open Buildalyzer +let [] TARGET_FRAMEWORK = "net6.0" + type FablePackage = { Id: string Version: string @@ -311,14 +313,19 @@ let getSourcesFromFablePkg (projFile: string) = | path -> [ path ] |> List.map Path.normalizeFullPath) -let private isUsefulOption (opt : string) = - [ "--define" - "--nowarn" - "--warnon" +let private extractUsefulOptionsAndSources (line: string) (accSources: string list, accOptions: string list) = + if line.StartsWith("-") then // "--warnaserror" // Disable for now to prevent unexpected errors, see #2288 // "--langversion" // See getBasicCompilerArgs - ] - |> List.exists opt.StartsWith + if line.StartsWith("--nowarn") || line.StartsWith("--warnon") then + accSources, line::accOptions + elif line.StartsWith("--define:") then + let defines = line.Substring(9).Split(';') |> Array.mapToList (fun d -> "--define:" + d) + accSources, defines @ accOptions + else + accSources, accOptions + else + (Path.normalizeFullPath line)::accSources, accOptions let excludeProjRef (opts: CrackerOptions) (dllRefs: IDictionary) (projRef: string) = let projName = Path.GetFileNameWithoutExtension(projRef) @@ -351,12 +358,8 @@ let getCrackedFsproj (opts: CrackerOptions) (projOpts: string[]) (projRefs: stri let dllName = getDllName line dllRefs.Add(dllName, line) src, otherOpts - elif isUsefulOption line then - src, line::otherOpts - elif line.StartsWith("-") then - src, otherOpts else - (Path.normalizeFullPath line)::src, otherOpts) + extractUsefulOptionsAndSources line (src, otherOpts)) let fablePkgs = let dllRefs' = dllRefs |> Seq.map (fun (KeyValue(k,v)) -> k,v) |> Seq.toArray @@ -397,7 +400,7 @@ let getProjectOptionsFromProjectFile = if Path.IsPathRooted f then f else Path.Combine(projDir, f) else f - fun (opts: CrackerOptions) projFile -> + fun (opts: CrackerOptions) (projFile: string) -> let manager = match manager with | Some m -> m @@ -406,25 +409,63 @@ let getProjectOptionsFromProjectFile = let options = AnalyzerManagerOptions(LogWriter = log) let m = AnalyzerManager(options) m.SetGlobalProperty("Configuration", opts.Configuration) + m.SetGlobalProperty("TargetFramework", TARGET_FRAMEWORK) for define in opts.FableOptions.Define do m.SetGlobalProperty(define, "true") manager <- Some m m - let analyzer = manager.GetProject(projFile) - // If the project targets multiple frameworks, multiple results will be returned - // For now we just take the first one with non-empty command - let result = - analyzer.Build() + let tryGetResult (getCompilerArgs: IAnalyzerResult -> string[]) (projFile: string) = + let analyzer = manager.GetProject(projFile) + let env = analyzer.EnvironmentFactory.GetBuildEnvironment(Environment.EnvironmentOptions(DesignTime=true,Restore=false)) + // If the project targets multiple frameworks, multiple results will be returned + // For now we just take the first one with non-empty command + let results = analyzer.Build(env) + results |> Seq.tryFind (fun r -> String.IsNullOrEmpty(r.Command) |> not) + |> Option.map (fun result -> + {| CompilerArguments = getCompilerArgs result + ProjectReferences = result.ProjectReferences + Properties = result.Properties |}) + + let csprojResult = + let csprojFile = projFile.Replace(".fsproj", ".csproj") + if IO.File.Exists(csprojFile) then + None + else + try + System.IO.File.Copy(projFile, csprojFile) + projFile.Replace(".fsproj", ".csproj") + |> tryGetResult (fun r -> + // Careful, options for .csproj start with / but so do root paths in unix + let reg = System.Text.RegularExpressions.Regex(@"^\/[^\/]+?(:?:|$)") + let comArgs = + r.CompilerArguments + |> Array.map (fun line -> + if reg.IsMatch(line) then + if line.StartsWith("/reference") then "-r" + line.Substring(10) + else "--" + line.Substring(1) + else line) + match r.Properties.TryGetValue("OtherFlags") with + | false, _ -> comArgs + | true, otherFlags -> + let otherFlags = otherFlags.Split(' ', StringSplitOptions.RemoveEmptyEntries) + Array.append otherFlags comArgs) + finally + File.safeDelete csprojFile + + let result = + csprojResult + |> Option.orElseWith (fun () -> projFile |> tryGetResult (fun r -> + // result.CompilerArguments doesn't seem to work well in Linux + System.Text.RegularExpressions.Regex.Split(r.Command, @"\r?\n"))) |> function | Some result -> result // TODO: Get Buildalyzer errors from the log | None -> $"Cannot parse {projFile}" |> Fable.FableError |> raise let projDir = IO.Path.GetDirectoryName(projFile) let projOpts = - // result.CompilerArguments doesn't seem to work well in Linux - System.Text.RegularExpressions.Regex.Split(result.Command, @"\r?\n") + result.CompilerArguments |> Array.skipWhile (fun line -> not(line.StartsWith("-"))) |> Array.map (compileFilesToAbsolutePath projDir) projOpts, Seq.toArray result.ProjectReferences, result.Properties @@ -437,6 +478,7 @@ let fullCrack (opts: CrackerOptions): CrackedFsproj = Process.runSync (IO.Path.GetDirectoryName opts.ProjFile) "dotnet" [ "restore" IO.Path.GetFileName opts.ProjFile + $"-p:TargetFramework={TARGET_FRAMEWORK}" for constant in opts.FableOptions.Define do $"-p:{constant}=true" ] |> ignore @@ -459,15 +501,7 @@ let easyCrack (opts: CrackerOptions) dllRefs (projFile: string): CrackedFsproj = getProjectOptionsFromProjectFile opts projFile let outputType = ReadOnlyDictionary.tryFind "OutputType" msbuildProps - let sourceFiles, otherOpts = - (projOpts, ([], [])) - ||> Array.foldBack (fun line (src, otherOpts) -> - if isUsefulOption line then - src, line::otherOpts - elif line.StartsWith("-") then - src, otherOpts - else - (Path.normalizeFullPath line)::src, otherOpts) + let sourceFiles, otherOpts = Array.foldBack extractUsefulOptionsAndSources projOpts ([], []) { ProjectFile = projFile SourceFiles = sourceFiles diff --git a/src/Fable.Cli/Util.fs b/src/Fable.Cli/Util.fs index c7a9c52e8d..27498a8110 100644 --- a/src/Fable.Cli/Util.fs +++ b/src/Fable.Cli/Util.fs @@ -248,6 +248,11 @@ module File = let isDirectoryEmpty dir = not(Directory.Exists(dir)) || Directory.EnumerateFileSystemEntries(dir) |> Seq.isEmpty + let safeDelete path = + try + File.Delete(path) + with _ -> () + let withLock (dir: string) (action: unit -> 'T) = let mutable fileCreated = false let lockFile = Path.Join(dir, "fable.lock") diff --git a/src/Fable.Transforms/Fable.Transforms.fsproj b/src/Fable.Transforms/Fable.Transforms.fsproj index 686c030764..09a8e7c96d 100644 --- a/src/Fable.Transforms/Fable.Transforms.fsproj +++ b/src/Fable.Transforms/Fable.Transforms.fsproj @@ -3,6 +3,7 @@ true netstandard2.0 + $(OtherFlags) --nowarn:3536