Closed jbrownson closed 10 years ago
Yes, you observations are correct. Cabal is unfortunately not very flexible when it comes to dealing with pre-processors or code generators that are not already hardcoded in Cabal. I recently discovered the following suggestion on how to define custom pre-processors in Cabal and maybe a somewhat similar approach could be used: https://gist.github.com/23Skidoo/7930870
If that doesn't work out, an alternative (and mega kludge) would be to define Haskell files as proxies for the compilation of the generated C sources. These Haskell files could call the C compiler using TH. (I know, it really would be a very bad hack..)
Is the project you are looking at for OS X (using Haskell with Objective-C) and will you use Xcode as well? If that is the case, you may like to consider an entirely different build architecture and let Xcode do all the build work. (This is especially attractive if you need to code sign your application or use similar features that are already automated by Xcode.) I have got a project set up like that and it is definitely a workable solution (with its own pros and cons).
Yeah it's going to start as OS X. I'm hoping that by the time I get it working there things will have advanced and I can bring it to iOS too :). If not it won't be too much effort to come up w/ an alternate solution for iOS.
I have a bit of experience using Xcode custom build rules. They're fairly limited, but something could probably be made to work with that. Good suggestion. I'll give that a try. I think the ObjectiveHaskell guys have that as part of the suggested workflow with some scripts and stuff provided.
Just an update. I've gotten the ObjectiveHaskell library to build w/ the latest Xcode for OS X. I'm going to mess w/ adding a build rule for language-c-inline too. I think the two will complement each other well.
I'm using language-c-inline in an Xcode project. Here is roughly how I'm doing it:
-staticlib
option.The ObjC interface presented by the Haskell model are classes defined using language-c-inline's objc_interface
splice. In order for Xcode to pick up the generated ObjC code, I use the following build rule to compile Haskell containing inline ObjC (they end up in the derived file directory) — also defining the corresponding Output Files in the build rule:
if /Users/chak/bin/ghc-7.8 -O -outputdir ${DERIVED_SOURCES_DIR} -odir ${OBJECT_FILE_DIR}-${CURRENT_VARIANT}/${CURRENT_ARCH} -c ${INPUT_FILE_PATH};
then
if [ -s ${INPUT_FILE_DIR}/${INPUT_FILE_BASE}_objc.h ]
then
mv -v ${INPUT_FILE_DIR}/${INPUT_FILE_BASE}_objc.h ${DERIVED_FILE_DIR}
fi
if [ -s ${INPUT_FILE_DIR}/${INPUT_FILE_BASE}_objc.m ]
then
mv -v ${INPUT_FILE_DIR}/${INPUT_FILE_BASE}_objc.m ${DERIVED_FILE_DIR}
fi
else
exit 1
fi
Moreover, we need an extra Copy Files build phase to perform linking of the Haskell objects files and to make the generated .h
files being visible to the rest of the project:
# Copy generated headers to the derived source for the entire project
mkdir -p ${PROJECT_DERIVED_FILE_DIR}
cp ${DERIVED_FILE_DIR}/*.h ${PROJECT_DERIVED_FILE_DIR}
#
# Extend the static library archive with all referenced Haskell libraries
# Seems that we can't use -threaded with -staticlib
mv ${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME} ${OBJECT_FILE_DIR}-${CURRENT_VARIANT}/${CURRENT_ARCH}/
/Users/chak/bin/ghc-7.8 -staticlib -o ${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME} ${OBJECT_FILE_DIR}-${CURRENT_VARIANT}/${CURRENT_ARCH}/${FULL_PRODUCT_NAME}
Alright I managed to spend a bit more time fiddling with this finally. It's working pretty well. I didn't follow exactly the way you did it for now. I just have one target, and I have a special extension (.m.hs) for Haskell files that are expected to generate language-c-inline output. I'm also trying to avoid the 7.8 dependency so I'm still adding a bunch of -lHS* linker flags for my dependencies.
Observations:
I think the solution to this "issue" is to not use Cabal and use some alternate build system instead, and Xcode seems to be sufficient. Want to close it? I'm sure I'll have other Qs as I proceed :).
hrm, just did a build again from a clean to make sure it works from scratch and realized that having different build rules for different extensions means all of the files w/ the extension for the first build rule will be processed before any of the files w/ the second extension, so you can't manage dependency orders between them properly... I need the separate extensions because I can't specify the .h and .m files if they're not going to be generated or Xcode gives errors. I suppose a workaround would be to create empty ones if they weren't generated by compiling the .hs file... wow.
I have a special extension (.m.hs) for Haskell files that are expected to generate language-c-inline output.
So, maybe I should make language-c-inline more clever about picking a filename for the generated Objective-C code. If, after stripping .hs
, a file already ends on .m
, it shouldn't add another .m
(and similarly convert the .m
into a .h
for the header).
- I can't use parallel builds because since the .hs files are being compiled one by one I have to have them in order of their dependency in the build list, and parallel builds don't guarantee the order will work out.
Yes, this is very annoying.
- IBOutlet stuff doesn't work. This isn't that big of a surprise since it's frankly a bit of a hack how Apple does it in the first place. A workaround could be to add support for it in the parser, but that is ugly. I might either avoid using xibs altogether, or do the interfaces that would use IBOutlets in regular .h files. Maybe I should open a separate issue for this?
I have got all IBOutlets
in regular .h
. files.
- I have some modules that have the pattern A.B and C.B. i.e. two modules with the same name under different paths. They were overwriting each other's .o files if I used -o, and if I didn't use -o then I couldn't predict the output file's name easily. I wound up including the ${INPUT_FILE_DIR} in the path under the derived files directory to work around the problem. It might be slightly nicer to break out the actual module path from the directory structure, but that would add complexity for no real gain.
I haven't run into this situation yet. I'm not sure how to solve that in a better way.
I think the solution to this "issue" is to not use Cabal and use some alternate build system instead, and Xcode seems to be sufficient. Want to close it? I'm sure I'll have other Qs as I proceed :).
Ok.
hrm, just did a build again from a clean to make sure it works from scratch and realized that having different build rules for different extensions means all of the files w/ the extension for the first build rule will be processed before any of the files w/ the second extension, so you can't manage dependency orders between them properly... I need the separate extensions because I can't specify the .h and .m files if they're not going to be generated or Xcode gives errors. I suppose a workaround would be to create empty ones if they weren't generated by compiling the .hs file... wow.
Oh :(
I noticed there are Makefiles in use for the examples that ship with language-c-inline as opposed to .cabal files. Being sure there was a good reason for this I set out to find out what it was by trying to use Cabal to build something that uses language-c-inline.
After looking through the Cabal source and experimenting with a custom Setup.hs I concluded that the challenge is that the standard hooks don't go deep enough. One would have to duplicate a lot of the compiler-specific code and make yourself compiler specific to make it work.
If you try to list the files that will be outputted as "c-sources:" entries in the cabal package then it will try to preprocess them before compiling the haskell (and therefore generating the files).
If you try to have a "x-language-c-inline:" custom entry then you have to reinvent a lot of wheels since you can't hook in on a per-file basis, and the linking happens in the same black box as the compilation so you can't hook in and just run the outputted .m files through a compiler and add them to the link process.
It seems like to integrate w/ Cabal the hooks would have to be extended in some way.
Am I about right here? I'm hoping to use language-c-inline for a medium sized project, but am having trouble deciding how to set up the build process. I suppose old-school Makefiles would get me going, but I'm hoping there's a better way. Has any work been done in this area that could help?