Skip to content

Commit

Permalink
Use Output.Exe as default when targeting Python
Browse files Browse the repository at this point in the history
  • Loading branch information
alfonsogarciacaro committed Nov 12, 2022
1 parent 90f4584 commit e6c040d
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 45 deletions.
19 changes: 9 additions & 10 deletions src/Fable.Cli/Main.fs
Original file line number Diff line number Diff line change
Expand Up @@ -320,16 +320,8 @@ type ProjectCracked(cliArgs: CliArgs, crackerResponse: CrackerResponse, sourceFi
let fableLibDir = Path.getRelativePath currentFile crackerResponse.FableLibDir
let watchDependencies = if cliArgs.IsWatch then Some(HashSet()) else None

let common = Path.getCommonBaseDir([currentFile; crackerResponse.FableLibDir])
let outputType =
// Everything within the Fable hidden directory will be compiled as Library. We do this since the files there will be
// compiled as part of the main project which might be a program (Exe) or library (Library).
if common.EndsWith(Naming.fableModules) then
Some "Library"
else
crackerResponse.OutputType

CompilerImpl(currentFile, project, opts, fableLibDir, ?watchDependencies=watchDependencies, ?outDir=cliArgs.OutDir, ?outType=outputType)
CompilerImpl(currentFile, project, opts, fableLibDir, crackerResponse.OutputType,
?outDir=cliArgs.OutDir, ?watchDependencies=watchDependencies)

member _.MapSourceFiles(f) =
ProjectCracked(cliArgs, crackerResponse, Array.map f sourceFiles)
Expand All @@ -349,6 +341,13 @@ OUTPUT TYPE: {result.OutputType}
%s{result.ProjectOptions.OtherOptions |> String.concat $"{Log.newLine} "}
%s{result.ProjectOptions.SourceFiles |> String.concat $"{Log.newLine} "}{Log.newLine}""")

// If targeting Python, make sure users are not compiling the project as library by mistake
// (imports won't work when running the code)
match cliArgs.CompilerOptions.Language, result.OutputType with
| Python, OutputType.Library ->
Log.always "Compiling project as Library. If you intend to run the code directly, please set OutputType to Exe."
| _ -> ()

let sourceFiles = result.ProjectOptions.SourceFiles |> Array.map File
ProjectCracked(cliArgs, result, sourceFiles)

Expand Down
3 changes: 2 additions & 1 deletion src/Fable.Cli/Pipeline.fs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ module Python =
| Some Py.Naming.sitePackages -> true
| _ -> false


// Everything within the Fable hidden directory will be compiled as Library. We do this since the files there will be
// compiled as part of the main project which might be a program (Exe) or library (Library).
let isLibrary = com.OutputType = OutputType.Library || Naming.isInFableModules com.CurrentFile
let isFableLibrary = isLibrary && List.contains "FABLE_LIBRARY" com.Options.Define

Expand Down
43 changes: 24 additions & 19 deletions src/Fable.Cli/ProjectCracker.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ open Fable.AST
open Globbing.Operators
open Buildalyzer

let [<Literal>] TARGET_FRAMEWORK = "net6.0"

type FablePackage =
{ Id: string
Version: string
Expand All @@ -33,7 +31,7 @@ type CacheInfo =
OutDir: string option
FableLibDir: string
FableModulesDir: string
OutputType: string option
OutputType: OutputType
Exclude: string list
SourceMaps: bool
SourceMapsRoot: string option
Expand Down Expand Up @@ -61,8 +59,13 @@ type CacheInfo =
| Some other -> this.GetTimestamp() > other.GetTimestamp()

type CrackerOptions(cliArgs: CliArgs) =
let projDir = IO.Path.GetDirectoryName cliArgs.ProjectFile
let targetFramework =
match Process.runSyncWithOutput projDir "dotnet" ["--version"] with
| Naming.StartsWith "7" _ -> "net7.0"
| _ -> "net6.0"
let fableModulesDir = CrackerOptions.GetFableModulesFromProject(projDir, cliArgs.OutDir, cliArgs.NoCache)
let builtDlls = HashSet()
let fableModulesDir = CrackerOptions.GetFableModulesFromProject(cliArgs.ProjectFile, cliArgs.OutDir, cliArgs.NoCache)
let cacheInfo =
if cliArgs.NoCache then None
else CacheInfo.TryRead(fableModulesDir, cliArgs.CompilerOptions.DebugMode)
Expand All @@ -74,6 +77,7 @@ type CrackerOptions(cliArgs: CliArgs) =
member _.FableLib: string option = cliArgs.FableLibraryPath
member _.OutDir: string option = cliArgs.OutDir
member _.Configuration: string = cliArgs.Configuration
member _.TargetFramework = targetFramework
member _.Exclude: string list = cliArgs.Exclude
member _.Replace: Map<string, string> = cliArgs.Replace
member _.PrecompiledLib: string option = cliArgs.PrecompiledLib
Expand All @@ -98,10 +102,10 @@ type CrackerOptions(cliArgs: CliArgs) =
IO.Path.Combine(baseDir, Naming.fableModules)
|> Path.normalizePath

static member GetFableModulesFromProject(projFile: string, outDir: string option, noCache: bool): string =
static member GetFableModulesFromProject(projDir: string, outDir: string option, noCache: bool): string =
let fableModulesDir =
outDir
|> Option.defaultWith (fun () -> IO.Path.GetDirectoryName(projFile))
|> Option.defaultWith (fun () -> projDir)
|> CrackerOptions.GetFableModulesFromDir

if noCache then
Expand All @@ -120,7 +124,7 @@ type CrackerResponse =
FableModulesDir: string
References: string list
ProjectOptions: FSharpProjectOptions
OutputType: string option
OutputType: OutputType
PrecompiledInfo: PrecompiledInfoImpl option
CanReuseCompiledFiles: bool }

Expand Down Expand Up @@ -320,6 +324,8 @@ let private extractUsefulOptionsAndSources (line: string) (accSources: string li
if line.StartsWith("--nowarn") || line.StartsWith("--warnon") then
accSources, line::accOptions
elif line.StartsWith("--define:") then
// When parsing the project as .csproj there will be multiple defines in the same line,
// but the F# compiler seems to accept only one per line
let defines = line.Substring(9).Split(';') |> Array.mapToList (fun d -> "--define:" + d)
accSources, defines @ accOptions
else
Expand Down Expand Up @@ -409,7 +415,7 @@ let getProjectOptionsFromProjectFile =
let options = AnalyzerManagerOptions(LogWriter = log)
let m = AnalyzerManager(options)
m.SetGlobalProperty("Configuration", opts.Configuration)
m.SetGlobalProperty("TargetFramework", TARGET_FRAMEWORK)
m.SetGlobalProperty("TargetFramework", opts.TargetFramework)
for define in opts.FableOptions.Define do
m.SetGlobalProperty(define, "true")
manager <- Some m
Expand All @@ -428,14 +434,16 @@ let getProjectOptionsFromProjectFile =
ProjectReferences = result.ProjectReferences
Properties = result.Properties |})

// Because Buildalyzer works better with .csproj, we first "dress up" the project as if it were a C# one
// and try to adapt the results. If it doesn't work, we try again to analyze the .fsproj directly
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")
csprojFile
|> tryGetResult (fun r ->
// Careful, options for .csproj start with / but so do root paths in unix
let reg = System.Text.RegularExpressions.Regex(@"^\/[^\/]+?(:?:|$)")
Expand Down Expand Up @@ -478,22 +486,16 @@ let fullCrack (opts: CrackerOptions): CrackedFsproj =
Process.runSync (IO.Path.GetDirectoryName opts.ProjFile) "dotnet" [
"restore"
IO.Path.GetFileName opts.ProjFile
$"-p:TargetFramework={TARGET_FRAMEWORK}"
$"-p:TargetFramework={opts.TargetFramework}"
for constant in opts.FableOptions.Define do
$"-p:{constant}=true"
] |> ignore

let projOpts, projRefs, msbuildProps =
getProjectOptionsFromProjectFile opts opts.ProjFile

// let targetFramework =
// match Map.tryFind "TargetFramework" msbuildProps with
// | Some targetFramework -> targetFramework
// | None -> failwithf "Cannot find TargetFramework for project %s" projFile

let outputType = ReadOnlyDictionary.tryFind "OutputType" msbuildProps

getCrackedFsproj opts projOpts projRefs outputType
ReadOnlyDictionary.tryFind "OutputType" msbuildProps
|> getCrackedFsproj opts projOpts projRefs

/// For project references of main project, ignore dll and package references
let easyCrack (opts: CrackerOptions) dllRefs (projFile: string): CrackedFsproj =
Expand Down Expand Up @@ -818,9 +820,12 @@ let getFullProjectOpts (opts: CrackerOptions) =
else Some("-r:" + r))
|> Seq.toArray

let outputType = mainProj.OutputType
let projRefs = projRefs |> List.map (fun p -> p.ProjectFile)
let otherOptions = Array.append otherOptions dllRefs
let outputType =
match mainProj.OutputType with
| Some "Library" -> OutputType.Library
| _ -> OutputType.Exe

let cacheInfo: CacheInfo =
{
Expand Down
12 changes: 9 additions & 3 deletions src/Fable.Cli/Util.fs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ module Process =
IO.Path.GetFullPath(dir) + (if isWindows() then ";" else ":") + currentPath

// Adapted from https://github.com/enricosada/dotnet-proj-info/blob/1e6d0521f7f333df7eff3148465f7df6191e0201/src/dotnet-proj/Program.fs#L155
let private startProcess (envVars: (string * string) list) workingDir exePath (args: string list) =
let private startProcess redirectOutput (envVars: (string * string) list) workingDir exePath (args: string list) =
let exePath, args =
if isWindows() then "cmd", "/C"::exePath::args
else exePath, args
Expand All @@ -351,6 +351,7 @@ module Process =
psi.WorkingDirectory <- workingDir
psi.CreateNoWindow <- false
psi.UseShellExecute <- false
psi.RedirectStandardOutput <- redirectOutput

// TODO: Make this output no logs if we've set silent verbosity
Process.Start(psi)
Expand All @@ -377,7 +378,7 @@ module Process =
fun (workingDir: string) (exePath: string) (args: string list) ->
try
runningProcess |> Option.iter kill
let p = startProcess envVars workingDir exePath args
let p = startProcess false envVars workingDir exePath args
runningProcess <- Some p
with ex ->
Log.always("Cannot run: " + ex.Message)
Expand All @@ -387,7 +388,7 @@ module Process =

let runSyncWithEnv envVars (workingDir: string) (exePath: string) (args: string list) =
try
let p = startProcess envVars workingDir exePath args
let p = startProcess false envVars workingDir exePath args
p.WaitForExit()
p.ExitCode
with ex ->
Expand All @@ -398,6 +399,11 @@ module Process =
let runSync (workingDir: string) (exePath: string) (args: string list) =
runSyncWithEnv [] workingDir exePath args

let runSyncWithOutput workingDir exePath args =
let p = startProcess true [] workingDir exePath args
p.WaitForExit()
p.StandardOutput.ReadToEnd()

[<RequireQualifiedAccess>]
module Async =
let fold f (state: 'State) (xs: 'T seq) = async {
Expand Down
2 changes: 0 additions & 2 deletions src/Fable.Transforms/Global/Compiler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ type Severity =
type OutputType =
| Library
| Exe
| Module
| Winexe

open FSharp.Compiler.Symbols
open Fable.AST
Expand Down
15 changes: 5 additions & 10 deletions src/Fable.Transforms/State.fs
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,12 @@ type Log =

/// Type with utilities for compiling F# files to JS.
/// Not thread-safe, an instance must be created per file
type CompilerImpl(currentFile, project: Project, options, fableLibraryDir: string, ?outDir: string, ?outType: string,
type CompilerImpl(currentFile, project: Project, options, fableLibDir: string, ?outType: OutputType, ?outDir: string,
?watchDependencies: HashSet<string>, ?logs: ResizeArray<Log>, ?isPrecompilingInlineFunction: bool) =

let outType = defaultArg outType OutputType.Exe
let logs = Option.defaultWith ResizeArray logs
let fableLibraryDir = fableLibraryDir.TrimEnd('/')
let outputType =
match outType with
| Some "Exe" -> OutputType.Exe
| Some "Module" -> OutputType.Module
| Some "Winexe" -> OutputType.Winexe
| _ -> OutputType.Library
let fableLibraryDir = fableLibDir.TrimEnd('/')

member _.Logs = logs.ToArray()
member _.WatchDependencies =
Expand All @@ -198,7 +193,7 @@ type CompilerImpl(currentFile, project: Project, options, fableLibraryDir: strin
member _.LibraryDir = fableLibraryDir
member _.CurrentFile = currentFile
member _.OutputDir = outDir
member _.OutputType = outputType
member _.OutputType = outType
member _.ProjectFile = project.ProjectFile
member _.SourceFiles = project.SourceFiles

Expand All @@ -211,7 +206,7 @@ type CompilerImpl(currentFile, project: Project, options, fableLibraryDir: strin
Path.Combine(Path.GetDirectoryName(currentFile), fableLibraryDir)
else fableLibraryDir
|> Path.getRelativeFileOrDirPath false file true
CompilerImpl(file, project, options, fableLibraryDir, ?outDir=outDir, ?outType=outType,
CompilerImpl(file, project, options, fableLibraryDir, outType, ?outDir=outDir,
?watchDependencies=watchDependencies, logs=logs, isPrecompilingInlineFunction=true)

member _.GetImplementationFile(fileName) =
Expand Down

0 comments on commit e6c040d

Please sign in to comment.