fsc(F#のCompiler)から Hello, World! したかった話
モチベーション
Cでは、c99 (standard C language compiler) を使って
$ c99 hello.c
として実行可能なバイナリファイルを生成する。
F#でも同様にして、単一のソースプログラムをコンパイラーに食わせてターゲットプログラムを生成するというのをやりたかった。
結論
無理そう
F#のCompilerの起動(macOSの場合)
.NET SDKのインストールを終えていれば
$ dotnet --list-sdks
とすることで、インストール済みのSDKのパスを得られる。
ここでシェル変数をsdk='/usr/local/share/dotnet/sdk/<VERSION>'
とし、以降はこのシェル変数を参照するものとする。
コンパイラーのパスは${sdk}/FSharp/fsc.dll
となる。
fsc.dll
は.NETアプリケーションの一種であるので、dotnet
コマンドを介して起動する必要がある。
$ dotnet ${sdk}/FSharp/fsc.dll --version
Microsoft (R) F# Compiler version 12.0.3.0 for F# 6.0
...
$ dotnet ${sdk}/FSharp/fsc.dll --help
Microsoft (R) F# Compiler version 12.0.3.0 for F# 6.0
Copyright (c) Microsoft Corporation. All Rights Reserved.
- OUTPUT FILES -
--out:<file> Name of the output file (Short form: -o)
--target:exe Build a console executable
--target:winexe Build a Windows executable
--target:library Build a library (Short form: -a)
--target:module Build a module that can be added to another assembly
--delaysign[+|-] Delay-sign the assembly using only the public portion of the strong name key
--publicsign[+|-] Public-sign the assembly using only the public portion of the strong name key, and mark the assembly as signed
--doc:<file> Write the xmldoc of the assembly to the given file
--keyfile:<file> Specify a strong name key file
--platform:<string> Limit which platforms this code can run on: x86, Itanium, x64, anycpu32bitpreferred, or anycpu. The default is
anycpu.
--nooptimizationdata Only include optimization information essential for implementing inlined constructs. Inhibits cross-module
inlining but improves binary compatibility.
--nointerfacedata Don't add a resource to the generated assembly containing F#-specific metadata
--sig:<file> Print the inferred interface of the assembly to a file
--allsigs Print the inferred interfaces of all compilation files to associated signature files
--nocopyfsharpcore Don't copy FSharp.Core.dll along the produced binaries
- INPUT FILES -
--reference:<file> Reference an assembly (Short form: -r)
--compilertool:<file> Reference an assembly or directory containing a design time tool (Short form: -t)
- RESOURCES -
--win32icon:<file> Specify a Win32 icon file (.ico)
--win32res:<file> Specify a Win32 resource file (.res)
--win32manifest:<file> Specify a Win32 manifest file
--nowin32manifest Do not include the default Win32 manifest
--resource:<resinfo> Embed the specified managed resource
--linkresource:<resinfo> Link the specified resource to this assembly where the resinfo format is <file>[,<string name>[,public|private]]
- CODE GENERATION -
--debug[+|-] Emit debug information (Short form: -g)
--debug:{full|pdbonly|portable|embedded} Specify debugging type: full, portable, embedded, pdbonly. ('full' is the default if no debuggging type specified
and enables attaching a debugger to a running program, 'portable' is a cross-platform format, 'embedded' is a
cross-platform format embedded into the output file).
--embed[+|-] Embed all source files in the portable PDB file
--embed:<file;...> Embed specific source files in the portable PDB file
--sourcelink:<file> Source link information file to embed in the portable PDB file
--optimize[+|-] Enable optimizations (Short form: -O)
--tailcalls[+|-] Enable or disable tailcalls
--deterministic[+|-] Produce a deterministic assembly (including module version GUID and timestamp)
--pathmap:<path=sourcePath;...> Maps physical paths to source path names output by the compiler
--crossoptimize[+|-] Enable or disable cross-module optimizations
- ERRORS AND WARNINGS -
--warnaserror[+|-] Report all warnings as errors
--warnaserror[+|-]:<warn;...> Report specific warnings as errors
--warn:<n> Set a warning level (0-5)
--nowarn:<warn;...> Disable specific warning messages
--warnon:<warn;...> Enable specific warnings that may be off by default
--consolecolors[+|-] Output warning and error messages in color
- LANGUAGE -
--langversion:{?|version|latest|preview} Display the allowed values for language version, specify language version such as 'latest' or 'preview'
--checked[+|-] Generate overflow checks
--define:<string> Define conditional compilation symbols (Short form: -d)
--mlcompatibility Ignore ML compatibility warnings
- MISCELLANEOUS -
--nologo Suppress compiler copyright message
--version Display compiler version banner and exit
--help Display this usage message (Short form: -?)
--@<file> Read response file for more options
- ADVANCED -
--codepage:<n> Specify the codepage used to read source files
--utf8output Output messages in UTF-8 encoding
--preferreduilang:<string> Specify the preferred output language culture name (e.g. es-ES, ja-JP)
--fullpaths Output messages with fully qualified paths
--lib:<dir;...> Specify a directory for the include path which is used to resolve source files and assemblies (Short form: -I)
--simpleresolution Resolve assembly references using directory-based rules rather than MSBuild resolution
--targetprofile:<string> Specify target framework profile of this assembly. Valid values are mscorlib, netcore or netstandard. Default -
mscorlib
--baseaddress:<address> Base address for the library to be built
--checksumalgorithm:{SHA1|SHA256} Specify algorithm for calculating source file checksum stored in PDB. Supported values are: SHA1 or SHA256
(default)
--noframework Do not reference the default CLI assemblies by default
--standalone Statically link the F# library and all referenced DLLs that depend on it into the assembly being generated
--staticlink:<file> Statically link the given assembly and all referenced DLLs that depend on this assembly. Use an assembly name e.g.
mylib, not a DLL name.
--pdb:<string> Name the output debug file
--highentropyva[+|-] Enable high-entropy ASLR
--subsystemversion:<string> Specify subsystem version of this assembly
--quotations-debug[+|-] Emit debug information in quotations
はじめてのコンパイル
そういうわけで以下のようなhello.fs
を作成し、
printfn "Hello, World!"
コンパイルしてみる。
$ dotnet ${sdk}/FSharp/fsc.dll hello.fs
すると普通に失敗する。
error FS0082: Could not resolve this reference. Could not locate the assembly "System.Runtime.Remoting.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245)
error FS0082: Could not resolve this reference. Could not locate the assembly "System.Runtime.Serialization.Formatters.Soap.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245)
error FS0082: Could not resolve this reference. Could not locate the assembly "System.Web.Services.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245)
error FS0082: Could not resolve this reference. Could not locate the assembly "System.Windows.Forms.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245)
🧐
調査
This behaviour is by design. The only reliable way to build a working coreclr app is by using the DotNET Sdk. the ```fsc program.fs```` build gesture doesn't work reliably. The reason for this is that computing the references and producing the correct runtime outputs are not built into the compiler. The compiler takes inut files and produces output files, adapting to different platforms is the work of the Sdk.
It would be fair to say that we take a pretty good crack at some of this work in fsi.exe, but we still rely on the sdk for doing most of that work.
On Linux the dotnet sdk has a way of locating the desktop binaries so wrap your build in an fsproj.
The command line for a simple hello, world processed by the dotnetsdk looks like, and this still won't produce runnable code it requires the build to have put the necessary dependencies and config files in the output directory.
Sure, I could probably go through and remove a ton of these lines and it would still compile, but they are all necessary to cover the main developer use case we build for. I think you'll find that C# has the exact same requirements.
無理そう
モチベーションで述べたやり方ではコンパイルができなさそうというのがわかった。
基本に則って、.fsproj
を基にしてやっていくことにする。