Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

ArtOfWarfare

macrumors G3
Original poster
Nov 26, 2007
9,670
6,211
I have a C program with a single source file. I need to get user input, so I figured I'd use readline.

Here's the abridged version of my source file, src.c:
Code:
// ... all my other headers ...
#include <readline/readline.h>

int main(int arc, char *argv[]) {
    char *input;
    while ((input = readline("> ")) != NULL) {
        // ... do useful stuff here
        free(input);
    }
    return 0;
}

Then I attempt to make and run it like this from bash:

Code:
make src -Ireadline && ./src

I'm realizing now that I'm more reliant on an IDE than I thought I was/would like to be given I'm getting tripped up on trying to compile a single source code with a single external library that it's dependent on.

Here are a few questions I have that I suspect may get at the root problem:

1 - What is the -I argument relative to? Is there an environment variable I should set that make will use or something, akin to PYTHONPATH in Python if you want it to look for your Python modules in a specific directory (Java has a similar environment variable, although I can't think of its name off the top of my head right now.)

2 - In #include, what is that relative to?

3 - I know that OS X doesn't come with gnu readline but instead has... libedit? I know that Python just handles it - if you import readline on a platform with libedit it uses that instead. Do I need to actually change anything?
 
A few things spring immediately to mind:
1a. What version of the C compiler or command line tools are you using?

1b. What was the error message, if any?

2. If <readline/readline.h> is intended to refer to a framework, then -F is more suitable than -I (may depend on C compiler version).

3. The args to 'make' are targets, not targets and options. You should probably read make's reference manual (I assume it's GNU make, so google for that), especially its env vars like CFLAGS.

4. For any non-trivial use of make, a makefile is your friend. Even when you think you don't need one. (Or more indirectly, anything that involves changing compiler options, that you intend to use more than once, is a non-trivial use of make.)
 
A few things spring immediately to mind:
1a. What version of the C compiler or command line tools are you using?

GNU Make 3.81
Apple LLVM version 6.0

1b. What was the error message, if any?

Code:
Undefined symbols for architecture x86_64:
  "_readline", referenced from:
      _main in src-144aab.o

4. For any non-trivial use of make, a makefile is your friend. Even when you think you don't need one. (Or more indirectly, anything that involves changing compiler options, that you intend to use more than once, is a non-trivial use of make.)

Yeah, I'm probably about at that point.

I was able to get my code to compile by changing the #include line to this:

Code:
#include <histedit.h>

And to compile it like this:

Code:
cc -g src.c -o src -ledit  && ./src

I found out to use those from this page:
https://www.cs.utah.edu/~bigler/code/libedit.html

Of course, they didn't properly escape their HTML so you need to inspect the source to see that the file they include is histedit.h.

Now when I compile it I get these warnings:

Code:
src.c:244:21: warning: implicit declaration of function 'readline' is invalid in
      C99 [-Wimplicit-function-declaration]
    while ((input = readline("> ")) != NULL) {
                    ^
src.c:244:19: warning: incompatible integer to pointer conversion assigning to
      'char *' from 'int' [-Wint-conversion]
    while ((input = readline("> ")) != NULL) {

And I get a segfault as soon as I hit enter after putting something in the prompt in my program.

I'm not sure if I'm somehow abusing LLVM and forcing it to compile when it shouldn't... or if I've linked something improperly... or if I have an actual logic error and I should check my program with valgrind.

Note that I shifted from make to cc - I couldn't get make to compile it.
 
...
Now when I compile it I get these warnings:

Code:
src.c:244:21: warning: implicit declaration of function 'readline' is invalid in
      C99 [-Wimplicit-function-declaration]
    while ((input = readline("> ")) != NULL) {
                    ^
src.c:244:19: warning: incompatible integer to pointer conversion assigning to
      'char *' from 'int' [-Wint-conversion]
    while ((input = readline("> ")) != NULL) {

And I get a segfault as soon as I hit enter after putting something in the prompt in my program.

I'm not sure if I'm somehow abusing LLVM and forcing it to compile when it shouldn't... or if I've linked something improperly... or if I have an actual logic error and I should check my program with valgrind.

Note that I shifted from make to cc - I couldn't get make to compile it.

Don't ignore the warnings. They're giving you significant information.

If there isn't any declaration of a function, then C will use an implicit one. That implicit declaration almost always has the wrong arg types and return values. For example, an implicitly declared function returns int, hence the warning about an implicit int-to-pointer conversion.

For the program to compile correctly, I think you need a valid declaration for readline(). One would think it should be in the included header, otherwise there's no point in including that header. But apparently something in that header is preventing an actual declaration from being processed by the compiler.

If the implicitly declared function doesn't match the actual function, then it's entirely possible that the args won't match, or even be close, and a crash-and-burn would result.


You may have to resort to looking at the preprocessor output (i.e. after includes but before actual compilation), in order to search for where readline() is declared. My first guess on problems like this is a conditional (#if or #ifdef) that's in the wrong state, thus a valid declaration in the original unprocessed header is being turned into nothing.

Another way to debug this is to make an Xcode project out of it, then look at the actual complete compiler commands (does Xcode still do that?). Then change your command-line to do the same thing.


You might also solve the problem by simply declaring the readline() function with the correct arg and return types, and don't bother trying to #include a header. If that's the only function you're using, then it's a simple quick fix to check viability.
 
Here are a few questions I have that I suspect may get at the root problem:

1 - What is the -I argument relative to? Is there an environment variable I should set that make will use or something, akin to PYTHONPATH in Python if you want it to look for your Python modules in a specific directory (Java has a similar environment variable, although I can't think of its name off the top of my head right now.)

-I is relative to $SDKROOT, according to the docs. (Xcode Build Setting Reference)

That's when Xcode is driving the build.
 
Do you have something against just calling gcc directly? That's what I do for small programs.

Both cc --version and gcc --version print out

Apple LLVM version 6.0

I had assumed they were both symlinked to the same thing, although now that I'm checking, cc is symlinked to clang, but gcc doesn't seem to be a symlink at all, even though it prints out about how it's LLVM in its --version output.

----------

Try:

Code:
gcc -o src src.c -lreadline

Using that and having

Code:
#include <readline/readline.h>

Appears to work... the compiler doesn't spit out warnings or errors anymore and my program isn't doing quite the right thing, but it isn't crashing, which suggests a logic error at this point.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.