Hacker News new | past | comments | ask | show | jobs | submit login
Building C Projects (nethack4.org)
191 points by rsaarelm on Nov 5, 2014 | hide | past | favorite | 45 comments



By contrast, the Plan 9 C compiler does use the header when linking. Header files contain a prama statement with library name, removing the need for linker flags.

See: http://plan9.bell-labs.com/sys/doc/comp.html


And when Rob Pike decided to create Go from that compiler, he just got rid of headers.


Because the Go team did the right thing by adopting modules, which are as old as C in the computing world.


What do you mean by "modules" here?


Go actually calls them packages.

I just prefer the term modules, as it was introduced by Mesa and CLU in the 70's.

Only languages based on C's primitive toolchain rely on basic separate compilation of translation units with textual includes for symbol definition.

Modular languages, that make use of better toolchains, couple the concept of separate compilation, with strong type checking across compilation boundaries and compiler managed metadata for the exported symbols.


Thanks for the explanation. Do you think the idea of implementation/interface separation, that C and Java allow easily, is also better in Go?


I didn't got your question.

Java packages are not that different from Go packages, in terms of CS concepts.

Except for the set of issues that are debated to death about Go, the language is quite modular in Mesa tradition.

Given Oberon's influence in Go's design[1], maybe you will find these books interesting,

From http://www.inf.ethz.ch/personal/wirth/ check "Algorithms and Data Structures", "Project Oberon", "Compiler Construction".

From http://ssw.jku.at/Research/Books/ check Object-Oriented Programming in Oberon-2.


Linking is a separate step from compiling. It's a lot easier and clearer to keep that distinction. Of course, it's not as convenient, which is why I've fallen for the lure of pragma's in the past - but at some point, it will come back to bite you in the behind (wrong library linked, no library linked because of unusual combination of preproc variables, etc).


Microsoft's MSVC has this feature too:

http://msdn.microsoft.com/en-us/library/7f0aews7.aspx


Some libraries do the same in Visual Studio, e.g. Boost. But you'd have a hard time finding the exact library name to link against there as well.


> instead of writing yet another wrapper around yet another set of poorly standardised existing tools, I'm using aimake, a build system I wrote originally for NetHack 4

Not sure I'll be able to use aimake, as it's not invented here ;-)

On a more serious note, it sounds a bit strange to claim this isn't built on other tools -- he uses various compilers and linkers, and other utilities for installation etc. Eg CMake might not be perfect, but at least I couldn't find any kind of rationale for why cmake or tup might not work perfectly fine for the purpose? Maybe I skimmed too quickly?


The author wanted a build system which does not ultimately generate a bunch of Makefiles. CMake and GNU Autotools both rely on Make to do real work.


Cmake doesn't have to use make. It can for example use ninja.


Was there any reason for that beyond ideology?


I think he said that make solved only a small, relatively easy part of the problem, and he didn't want to have to work around the limitations of make and the syntax of makefiles. He also mentions that different make implementations have different idiosyncrasies that he'd have to work around. Also, I think he wanted to preserve more information about the dependency tree between the various steps, instead of having multiple tools each making their own half-assed (or completely manual) attempts at deducing that information.


Considering the glut of C build systems (often built on top of GNU Makefiles) as proof that this is a complicated field, this is a surprisingly undiscussed aspect of programming. Thanks for this link!


For the preprocessing part of the problem this might be useful: https://news.ycombinator.com/item?id=8356100


This is probably the first time I really get what is happening during a build (despite having written a few C programs and Makefiles). Thanks for writing!


An interesting treatise, however the entire "Standard directory detection" part gives me the willies.


If anyone ever finds a linux C program that doesn't quite do what they need, my advice is this as I've had great success with it in the past modifying ntfs3g and the like..

Download the source package and open it in NetBeans! For what I needed clicking build JUST WORKED. For my purposes it was a short trip to adding some new arg commands and modifying functionality, then building and packaging it back into a .deb ready to go. I'm not sure if this is still the case, but NetBeans was pretty fantastic for this just 2 years ago.


That won't always work, because often there are OS specific files. E.g. different implementations of "mmap" for Unix and Windows or strlcmp and snprintf implementations for platforms that don't provide them their self.

Also often certain headers need to be generated by the build system ("config.h.in" for version information, "export.h.in" for export macros).


For goodness sake, keep writing these type of articles! I am now digging into "Memory management in C programs" and I hope the rest of the articles are as good as these two.


"The algorithm used by the vast majority of preprocessors is very simple: the preprocessor has a hardcoded list of directories, which the user can add to, and the preprocessor will scan them in sequence until it finds the file it's looking for. In the case of include "file.h", it will also scan the directory containing the source file."

At least for gcc, that is not quite correct. It also has a list of user directories.

The user list starts as a list containing only the current directory, but can be extended with command-line options. See https://gcc.gnu.org/onlinedocs/cpp/Search-Path.html


> C is a compiled language[...]

Not off to a great start :/


Eh?

Um...that's true?


Compiled vs interpreted is not a property of the language. It's a (vague) property of a particular implementation.


> Compiled vs interpreted is not a property of the compiler.

I assume you meant "language." You're of course correct, but if ever there were a language that deserved to be called "compiled," C is it. It truly was designed to be compiled, and who in their right mind would bother interpreting it?

And before you ask, no, people who debug C are rarely in their right minds. :)


> ...who in their right mind would bother interpreting it?

Um... me?

https://code.google.com/p/picoc/


That's really an interpreter (as opposed to using a bytecode vm or some such)? How does it deal with memory allocation, alignment etc? And (I don't have a proper dev setup on hand) -- the obvious question -- can it run itself, in itself in itself? (And itself in tcc in itself?) ;-)


It's a real interpreter. It runs directly from the source code - it doesn't even store a parse tree or bytecode. Memory allocation and alignment is done in the standard C ways. It's designed as a scripting variant of C so it doesn't implement 100% of the C standard (eg. bitfields), so no it's not self-hosting.


Fascinating, I'll have to look at it more closely when I've got a usable command line available. I would not have thought it was feasible for a useful subset of C -- but then I tend to forget that it is a rather simple language at heart (eg: after running the pre-prosessor).


Do you support GOTOs?

#ifdefs?

aligment #pragmas?

#includes?

extern?

If you don't--which is integral to how C works--you've created a cool C-like language, but not C.

C is quite nearly just a high-level assembler with syntax sugar.


Yes, of course, I've fixed my post. Thanks.

> if ever there were a language that deserved to be called "compiled," C is it.

I was only clarifying the other guy's comment, but I do appreciate the desire to be careful with the meaning of technical terms, lest they get diluted and become less useful.

> It truly was designed to be compiled, and who in their right mind would bother interpreting it?

Lots of people can (and do!) interpret C, or some subset, superset, or abstraction of C, for a great many purposes. Consider debuggers, IDEs, formal verification systems, build systems, security tools, etc.


Physicists at CERN :), their analysis framework ROOT is bundled with a C/C++ interpreter. I had only occasion to use it once, but it was a fairly pleasant experience.


> who in their right mind would bother interpreting it?

In some cases interpreting C can be faster and more convenient: http://www.chrisseaton.com/rubytruffle/cext/.


> and who in their right mind would bother interpreting it?

https://www.softintegration.com/

> It truly was designed to be compiled

I'm not sure - what qualities of a language make it designed for compilation?


> I'm not sure - what qualities of a language make it designed for compilation?

I'd say no high level abstractions, no need for a runtime keeping track of what you're doing (garbage collector, bound checks etc...)

The standard also describes a bunch of undefined/undetermined behaviours to let the compiler generate machine code as efficient as possible and without the need for a runtime (or as small a runtime as possible).

None of those things forbid an interpreted implementation but it makes less sense to leave those gotchas into the language if you don't have to talk to the CPU directly, it makes more sense to let the interpreter take care of the architecture-dependent details.

But pedants are going to be pedants.


> what qualities of a language make it designed for compilation?

That a language that compiled to good machine code was one of the main goals of the people who designed it. I was making a historical statement, not a qualitative one.


Meanwhile, in reality, C is a compiled language.


People tend to compile their C code. It's probably more accurate to speak of tendencies in how it's implemented and used then to assign attributes to the language.


No, it's accurate to describe reality rather than hypotheticals.


And the reality is that languages are not compiled or interpreted, but there are implementation of language compilers or interpreters. The C language isn't "compiled", but there are some implementations of C compilers, and some implementations of C interpreters.

Another good example, is an old language, "BASIC", which a lot of people used the interpreted version, but, as early as 1987, I used a BASIC compiler which provided as much as a 100x increase in performance over the common interpreter that came with my system.

Python is another great example - lots of Python interpreters out there, but Cython lets you compile your python code to machine code.


So, what you're saying is that C is (usually) compiled?


FWIW, from the post:

> A disclaimer: this post focuses on how things typically work on modern consumer operating systems (especially Linux and Windows), rather than trying to cover all possible obscure systems. C doesn't have to work like this. It just normally does in practice.


And then there's the rest of the world:

Python: Interpreted language, right? Then what are all those .pyc files doing? You can say it's compiled into an interpreted form, but then JVM bytecode comes along, which is interpreted right up until you find an implementation that JITs. Or is it only compiled if you save the compiled form to disk, like you do with gcj?

Scheme: An interpreted language in Guile, a compiled language in Stalin?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: