cabal icon indicating copy to clipboard operation
cabal copied to clipboard

Explicitly support building with -fno-code

Open nh2 opened this issue 12 years ago • 27 comments

Summary by @ezyang. There is no way to ask Setup to just typecheck a package. It sort of works if you pass -fno-code via --ghc-options but that's purely accidental.


I use cabal build --ghc-options="-Wall -fno-code" to quickly get all errors/warnings from a project and show them in an editor.

However, when I have something like this

executable Shared.so
  hs-source-dirs:
    apps
  main-is:
    Shared.hs
  build-depends:
      base >= 4 && <= 5
  ghc-options:
    -optl-shared -optc-DMODULE=Shared -no-hs-main -fPIC -shared -dynamic
  cc-options: -DMODULE=Shared -shared
  ld-options: -shared /home/niklas/opt/haskell-7.4/lib/ghc-7.4.2/libHSrts-ghc7.4.2.so

in my cabal file that only builds a shared object, I get:

Warning: the following files would be used as linker inputs, but linking is not being done: dist/build/...
ghc: no input files
Usage: For basic information, try the `--help' option.

Should we get this warning although we explicitly said -fno-code?

If yes, can we add an option to disable this kind of warning so that it does not clutter the warnings I am actually interested in (to show in the editor)?


Update:

(The original title was Add option to disable non-linking warnings with -fno-code.)

I just realized that there is an actual bug in this:

After the warning, due to the ghc error cabal will just stop and build no further. This should not happen as there is no code to build!

nh2 avatar Jan 11 '13 22:01 nh2

This even happens when you don't have a configuration as above, but a way more normal one, like hspec's cabal file.

A normal build looks like this:

Building hspec-1.4.3...                                                                     
Preprocessing library hspec-1.4.3...
[ 1 of 14] Compiling Test.Hspec.Compat ( src/Test/Hspec/Compat.hs, dist/build/Test/Hspec/Compat.o )
...
[14 of 14] Compiling Test.Hspec.QuickCheck ( src/Test/Hspec/QuickCheck.hs, dist/build/Test/Hspec/QuickCheck.o )
In-place registering hspec-1.4.3...
Preprocessing executable 'hspec-discover' for hspec-1.4.3...
[1 of 2] Compiling Run              ( hspec-discover/src/Run.hs, dist/build/hspec-discover/hspec-discover-tmp/Run.o )
[2 of 2] Compiling Main             ( hspec-discover/src/Main.hs, dist/build/hspec-discover/hspec-discover-tmp/Main.o )
Linking dist/build/hspec-discover/hspec-discover ...

But cabal build --ghc-options="-fno-code" gives:

Building hspec-1.4.3...                                                
Preprocessing library hspec-1.4.3...
[ 1 of 14] Compiling Test.Hspec.Compat ( src/Test/Hspec/Compat.hs, nothing )
...
[14 of 14] Compiling Test.Hspec.QuickCheck ( src/Test/Hspec/QuickCheck.hs, nothing )
/usr/bin/ar: dist/build/Test/Hspec.o: No such file or directory

Note this only breaks if the code hasn't been built yet (e.g. after cabal clean). If it has been built before, cabal build --ghc-options="-fforce-recomp -fno-code" correctly yields:

Building hspec-1.4.3...
Preprocessing library hspec-1.4.3...
[ 1 of 14] Compiling Test.Hspec.Compat ( src/Test/Hspec/Compat.hs, nothing )
...
[14 of 14] Compiling Test.Hspec.QuickCheck ( src/Test/Hspec/QuickCheck.hs, nothing )
In-place registering hspec-1.4.3...
Preprocessing executable 'hspec-discover' for hspec-1.4.3...
[1 of 2] Compiling Run              ( hspec-discover/src/Run.hs, nothing )
[2 of 2] Compiling Main             ( hspec-discover/src/Main.hs, nothing )

I think this is a bug - cabal build --ghc-options="-fno-code" should always work, no matter if code had been generated before!

nh2 avatar Jan 13 '13 09:01 nh2

@23Skidoo Can you reproduce this with some of your packages (or hspec as above)?

nh2 avatar Jan 13 '13 09:01 nh2

@nh2 I'm a bit swamped right now, will look at this later.

23Skidoo avatar Jan 13 '13 14:01 23Skidoo

I was thinking into this direction (https://github.com/nh2/cabal/commit/0c51929cf6c3a7fd1850daf4124b3c28da1175db) but then realized that what I want is not that easy: You could perform a "read-only" typecheck with cabal to build the library, but as soon as you want to typecheck an executable (that depends on your own library and is in a different directory to avoid repeated compilation), you actually need the result of the library compilation.

Actually, you only need the *.hi files; it should be possible to generate them without further code generation, but I don't know how (see http://stackoverflow.com/questions/14306934/haskell-how-to-only-generate-hi-file-with-ghc).

nh2 avatar Jan 13 '13 19:01 nh2

In the mean time, an easy work around would be #1177 - making a full cabal build instant by skipping linking if not necessary. (This could also be accomplished with -c, but that still performs unnecessary registering and unnecessarily links .so files.) On success (otherwise we have an error anyway), we can immediately run cabal build --ghc-options="-fforce-recomp -Wall -fno-code" which will work now since all necesssary files already exist.

However, this doesn't work as described in my original bug report: ghc reports ghc: no input files and terminates before the remaining warnings can be generated.

nh2 avatar Jan 13 '13 19:01 nh2

https://github.com/nh2/cabal/compare/no-code-link-shared is a proposal to skip linking on -fno-code. It fixes the error described.

An explicit cabal option to disable this step might be better, though.

nh2 avatar Jan 13 '13 21:01 nh2

I just added an option -fwrite-interface, to dump interface files precisely when you want to do something like typecheck a library, and then typecheck a dependent executable.

ezyang avatar Jun 27 '14 08:06 ezyang

@ezyang Can you explain a bit more how this is intended to be used?

nh2 avatar Jul 15 '14 21:07 nh2

If you are planning on doing only typechecking cycles, and you have a library + executable Cabal file, run -fno-code and -fwrite-interface to typecheck the library, and then you will be able to type check the executable (because the library hi files are available.) Needs some UI polish, obviously!

ezyang avatar Jul 15 '14 22:07 ezyang

I also see this error when building even static libraries or executables. Would be happy to hack on it if that's helpful, although I haven't looked at cabal's code before. Not sure if this is a beginner-level problem or not.

asivitz avatar Sep 04 '14 18:09 asivitz

This issue seems to persist, and is currently causing minor frustration in using SublimeHaskell, whose default build mechanism uses two builds: first a standard cabal build and then cabal build -v0 --ghc-options="-fforce-recomp -Wall -fno-code" to gather warnings. This issue is causing SublimeHaskell to display a bothersome error pane every time a project is recompiled (which happens every save). It's a minor issue, but a fix would be appreciated.

Here is the relevant SublimeHaskell issue: https://github.com/SublimeHaskell/SublimeHaskell/issues/158

alilleybrinker avatar Feb 03 '15 06:02 alilleybrinker

A tip from @rwbarton just now on #ghc, we could use -e which is like ghci, but terminates.

TH works in ghci (due to use of bytecode), so this should be almost as fast as -fno-code (it still generates the bytecode which is some code, but in my experience that's much faster than even -O0).

nh2 avatar Nov 12 '16 02:11 nh2

I'd like to add another vote for this ticket. I'm not sure what the UI should be (cabal typecheck or cabal build --typecheck come to mind). Whether it's called "typecheck", "no-code", "fast", etc is not a huge issue (although "no-code" never seemed very intuitive to me personally). The important thing in my mind is to have this as an explicit cabal option somewhere so the fastest compile loop is more discoverable.

mightybyte avatar Dec 01 '16 16:12 mightybyte

the -e option doesn't work for me, because I'm using cabal build as a replacement for ghci in a project where the interpreter can't load at all because of linking errors (i.e. and thus cabal repl, ghcid, dante, etc can't help).

sboosali avatar Jan 19 '18 23:01 sboosali

I'd like to add another vote for this ticket. I'm not sure what the UI should be (cabal typecheck or cabal build --typecheck come to mind). Whether it's called "typecheck", "no-code", "fast", etc is not a huge issue (although "no-code" never seemed very intuitive to me personally). The important thing in my mind is to have this as an explicit cabal option somewhere so the fastest compile loop is more discoverable.

I like this idea! Any update for this?

ice1000 avatar Nov 05 '18 09:11 ice1000

same. having a subcommand is better for discovery too.

e.g. someone who tab-completes cabal t for testing will notice typecheck and they start using it.

also, even if though it's (currently) equivalent to just a few options, it's one less thing a user needs to do, and they can trust that it's the right way to do it.

On Mon, Nov 5, 2018, 01:01 Tesla Ice Zhang <[email protected] wrote:

I'd like to add another vote for this ticket. I'm not sure what the UI should be (cabal typecheck or cabal build --typecheck come to mind). Whether it's called "typecheck", "no-code", "fast", etc is not a huge issue (although "no-code" never seemed very intuitive to me personally). The important thing in my mind is to have this as an explicit cabal option somewhere so the fastest compile loop is more discoverable.

I like this idea! Any update for this?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/haskell/cabal/issues/1176#issuecomment-435800938, or mute the thread https://github.com/notifications/unsubscribe-auth/ACNoMYL71RKmKlWpNLuh9UZ7nimie7JSks5ur_6BgaJpZM4AW0uK .

sboosali avatar Nov 06 '18 10:11 sboosali

+1 for adding a subcommand (cabal typecheck) from me.

23Skidoo avatar Nov 06 '18 11:11 23Skidoo

In v1-cabal, this works for just type-checking:

cabal v1-build --builddir=dist-no-code \
	  --ghc-options=-fno-code \
	  --ghc-options=-fwrite-interface

In v2-cabal, it seems that passing new --ghc-options will cause ALL THE DEPENDENCIES to be rebuilt. That defeats the purpose of quick type-checking.

Example: We build ordinarily:

$ cabal v2-build -w ghc-8.0.2
Resolving dependencies...
Build profile: -w ghc-8.0.2 -O1
In order, the following will be built (use -v for more details):
 - haskell-lexer-1.1 (lib:haskell-lexer) (requires build)
 - prettyprinter-1.7.0 (lib) (requires build)
 - pretty-show-1.10 (lib) (requires build)
 - prettyprinter-ansi-terminal-1.1.2 (lib) (requires build)
 - BNFC3-3.0 (lib) (configuration changed)
 - BNFC3-3.0 (exe:bnfc3) (configuration changed)
...

After that, we want to just type-check:

$ cabal v2-build -w ghc-8.0.2 --ghc-options=-fno-code --ghc-options=-fwrite-interface
Resolving dependencies...
Build profile: -w ghc-8.0.2 -O1
In order, the following will be built (use -v for more details):
 - base-orphans-0.8.4 (lib) (requires build)
 - clock-0.8.2 (lib) (requires build)
 - colour-2.3.5 (lib) (requires build)
 - containers-0.6.4.1 (lib) (requires build)
 - haskell-lexer-1.1 (lib:haskell-lexer) (requires build)
 - alex-3.2.6 (exe:alex) (requires build)
 - microlens-0.4.12.0 (lib) (requires build)
 - mtl-2.2.2 (lib) (requires build)
 - stm-2.5.0.0 (lib) (requires build)
 - string-qq-0.0.4 (lib) (requires build)
 - transformers-compat-0.6.6 (lib) (requires build)
 - extra-1.7.9 (lib) (requires build)
 - ansi-terminal-0.11 (lib) (requires build)
 - th-abstraction-0.4.2.0 (lib) (requires build)
 - binary-0.8.8.0 (lib) (requires build)
 - happy-1.20.0 (exe:happy) (requires build)
 - transformers-base-0.4.5.2 (lib) (requires build)
 - microlens-mtl-0.2.0.1 (lib) (requires build)
 - ansi-wl-pprint-0.6.9 (lib) (requires build)
 - microlens-th-0.4.3.9 (lib) (requires build)
 - text-1.2.4.1 (lib) (requires build)
 - monad-control-1.0.2.3 (lib:monad-control) (requires build)
 - optparse-applicative-0.16.1.0 (lib) (requires build)
 - prettyprinter-1.7.0 (lib) (requires build)
 - pretty-show-1.10 (lib) (requires build)
 - prettyprinter-ansi-terminal-1.1.2 (lib) (requires build)
 - BNFC3-3.0 (lib) (configuration changed)
 - BNFC3-3.0 (exe:bnfc3) (configuration changed)

^C is the only answer to such madness!

andreasabel avatar Apr 21 '21 19:04 andreasabel

@andreasabel I think the issue you mention is #3579: ghc-options is not available in top-level local options (but it still is in per-package options)

fgaz avatar Aug 28 '21 16:08 fgaz

The command toonly typecheck suggested by @andreasabel: cabal v2-build --ghc-options=-fno-code --ghc-options=-fwrite-interface

is more useful after #7973, cause now ghc-options applies only to all local packages. So change it does not trigger the recompilation of dependencies (aka remote packages).

@nh2 would be it enough for your use case? how could be it improved to fit it? create a new command would be difficult to get done

jneira avatar May 01 '22 13:05 jneira

This is a long awaited improvement. I built a shiny new Cabal executable and tried it out today.

+

It works for a single component:
% ~/.cabal/bin/cabal init --non-interactive
Warning: this is a debug build with assertions enabled.
[Log] Guessing dependencies...
[Log] Using cabal specification: 3.6
[Warning] unknown license type, you must put a copy in LICENSE yourself.
[Log] Creating fresh file CHANGELOG.md...
[Log] Creating fresh directory ./app...
[Log] Creating fresh file app/Main.hs...
[Log] Creating fresh file x.cabal...
[Warning] No synopsis given. You should edit the .cabal file and add one.
[Info] You may want to edit the .cabal file and add a Description field.

% ~/.cabal/bin/cabal build --ghc-options=-fno-code --ghc-options=-fwrite-interface
Warning: this is a debug build with assertions enabled.
Resolving dependencies...
Build profile: -w ghc-8.10.7 -O1
In order, the following will be built (use -v for more details):
 - x-0.1.0.0 (exe:x) (first run)
Warning: this is a debug build with assertions enabled.
Configuring executable 'x' for x-0.1.0.0..
Warning: this is a debug build with assertions enabled.
Preprocessing executable 'x' for x-0.1.0.0..
Building executable 'x' for x-0.1.0.0..
[1 of 1] Compiling Main             ( app/Main.hs, nothing )
% echo $?
0

It does not work when there are multiple interdependent components:
% ~/.cabal/bin/cabal init --non-interactive --libandexe
Warning: this is a debug build with assertions enabled.
[Log] Guessing dependencies...
[Log] Guessing dependencies...
[Log] Using cabal specification: 3.6
[Warning] unknown license type, you must put a copy in LICENSE yourself.
[Log] Creating fresh file CHANGELOG.md...
[Log] Creating fresh directory ./src...
[Log] Creating fresh file src/MyLib.hs...
[Log] Creating fresh directory ./app...
[Log] Creating fresh file app/Main.hs...
[Log] Creating fresh file x.cabal...
[Warning] No synopsis given. You should edit the .cabal file and add one.
[Info] You may want to edit the .cabal file and add a Description field.

% ~/.cabal/bin/cabal build --ghc-options=-fno-code --ghc-options=-fwrite-interface
Warning: this is a debug build with assertions enabled.
Resolving dependencies...
Build profile: -w ghc-8.10.7 -O1
In order, the following will be built (use -v for more details):
 - x-0.1.0.0 (lib) (first run)
 - x-0.1.0.0 (exe:x) (first run)
Warning: this is a debug build with assertions enabled.
Configuring library for x-0.1.0.0..
Warning: this is a debug build with assertions enabled.
Preprocessing library for x-0.1.0.0..
Building library for x-0.1.0.0..
[1 of 1] Compiling MyLib            ( src/MyLib.hs, nothing, /tmp/x/dist-newstyle/build/x86_64-linux/ghc-8.10.7/x-0.1.0.0/build/MyLib.dyn_o )
/usr/bin/ar: dist-newstyle/build/x86_64-linux/ghc-8.10.7/x-0.1.0.0/build/MyLib.o: No such file or directory
Error: cabal: Failed to build x-0.1.0.0 (which is required by exe:x from
x-0.1.0.0).

% echo $?
1

It seems that Cabal tries to link the dependent components (such as internal libraries), but there is nothing to link because nothing was built!

 

I cannot wait for this improvement to be released!

kindaro avatar Jul 10 '22 12:07 kindaro

If adding a new command cabal typecheck (or a special flag cabal build --typecheck) is still on the table, I say it should behave like this:

  • Accept the same options as cabal build. (So that the one can be easily swapped for the other.)
  • Produce the same standard error as cabal build. (So that it calls out all errors.)
  • Produce no object files.
  • Run faster.

Even though thanks to haskell-language-server one needs to call Cabal only rarely, it is still a good sanity check for when one is about to commit some code, for continuous integration or whenever haskell-language-server gets stuck or cannot start.

kindaro avatar Jul 10 '22 13:07 kindaro

Yes, it should be like -O0, in particular, use its own build directory. The main annoyance with the current workaround (a separate cabal.project file with suitable ghc-options) is that builddir isn't accepted in cabal.project, and has to be passed on the command line manually all the time, like in https://github.com/agda/agda/blob/a47f8762978e878ecf6ed6081fa3772acc3499d1/Makefile#L238 . In contrast -O0 stores build artefacts in its own .../noopt/... folders so that the regular build artefacts (-O1) are not overwritten.

andreasabel avatar Jul 11 '22 08:07 andreasabel

@andreasabel sorry for a basic question, but why do you need to store -no-code artifacts separately? For -O0, a separate directory is needed so that you don't rebuild every time you switch configuration, but that shouldn't apply to -no-code as it shouldn't, in theory, generate any different artifacts?

ulysses4ever avatar Jul 11 '22 11:07 ulysses4ever

it shouldn't, in theory, generate any different artifacts?

The typechecking-only build tree contains the cabal configuration, interface files, and even some object files (e.g. Paths_*.o, setup/Main.o, modules using TemplateHaskell). Until evidence to the contrary is produced, I must assume there are differences.

andreasabel avatar Jul 12 '22 08:07 andreasabel

@Mikolaj you added this issue to the project about replacing v1-commands, but I don't quite see which v1 functionality this would replace. At least, I don't think there's anything better in v1 than already shown here --no-code, which works perfectly with v2. The issue is about wrapping it into a prettier interface, but that's unrelated to v1 I think?

So, how about we remove this from the project?

ulysses4ever avatar Jul 11 '23 01:07 ulysses4ever

Yes, I guess the workaround is good enough not to make this issue a blocker (though probably not to close the issue and, in any case, that's for the affected users to opine on). Let me remove this ticket from the https://github.com/haskell/cabal/projects/12 project. Thanks.

Mikolaj avatar Jul 11 '23 14:07 Mikolaj