3

I am having an error in the preprocessing step when compiling a C program.

The odd thing is that I can run the preprocessing with no errors or warnings using:

gcc -I/usr/local/libpng-1.6.24/include -Wall -std=c99 lines.c -E -o lines

but using the form below give me errors:

cpp -I/usr/local/libpng-1.6.24/include -std=c99 lines.c -o lines

I am assuming that cpp is run as part of gcc what makes it more wierd.

For information see below the error I am getting -only one of them, I have many of them of the same sort:

In file included from lines.c:1:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/stdio.h:65:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/Availability.h:172:
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/AvailabilityInternal.h:15284:10: error:
    unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_11

Needless to notice that the file with the error is a Apple standard file. I must say also that it seems all right to me having all terminating #endif matching their #if directives

Any idea what could be happening here? Or way forward to track down the error?

PS By the way I am running this in OSX with developer tools provided standard by Apple.

--

Adding additional information as a response to some comments:

The source code of lines.c https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-s096-introduction-to-c-and-c-january-iap-2013/final-project/starter-kit/lines.c and to have more context of where the source code is used you might want to see here https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-s096-introduction-to-c-and-c-january-iap-2013/final-project/starter-kit/

If I run gcc without the option -E actually I get a linking problem because of my folder structure but the command below will work with no error -but I do not think this information was relevant for the question because I want to compare like for like and option -E stops after preprocessing and cpp does only preprocessing:

gcc -I/usr/local/libpng-1.6.24/include -L/usr/local/libpng-1.6.24/lib -Wall -std=c99 lines.c -lpng -o lines

--

I have upgraded my Xcode from 7.3 to 8.2.1 and the command line tools from Command_Line_Tools_OS_X_10.11_for_Xcode_7.3 to Command_Line_Tools_macOS_10.12_for_Xcode_8.2 and gcc continues working and cpp still triggering an error. Bu tthis time the error changes a little bit. See below

In file included from lines.c:1:
In file included from /usr/include/stdio.h:65:
In file included from /usr/include/Availability.h:184:
/usr/include/AvailabilityInternal.h:20265:10: error: #else without #if
    #else
/usr/include/AvailabilityInternal.h:20832:10: error: unterminated conditional directive
    #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_11_4
MartiSanchez
  • 61
  • 1
  • 7
  • 3
    -E conceals the error because it doesn't continue to the compile step, so that's not relevant. What happens if you compile with `-Wall` but not `-E`? – Carey Gregory Feb 03 '17 at 19:51
  • And you need to post `lines.c`, or at least the first section showing all the `#includes`. – Carey Gregory Feb 03 '17 at 19:52
  • You don't seem to be using the macOS Sierra (10.12) version of the SDK. The later version does not have a `#if` line close to line 15284 (out of 21302 lines). I only have the later version currently installed, so I can't look for you easily. But at 20k lines, the file is not easy reading (and is very boring reading). There seem to be over 2500 lines in the file that are `#if`, `#ifdef`, `#else`, `#elif` or `#endif` lines. I'm not sure if a tool to analyze such lines would help you. (Contact me if it would — see my profile.) – Jonathan Leffler Feb 03 '17 at 20:10
  • It is surprising that the header doesn't work. It is used a fair amount — I just compiled a simple C program with `#include ` and `#include ` and the `-H` option and `AvailabilityInternal.h` is included in that. Are you able to compile anything at all? – Jonathan Leffler Feb 03 '17 at 20:10
  • On response to Carey I have added additional information on my original post – MartiSanchez Feb 03 '17 at 20:13
  • Also, running `cpp … lines.c -o lines` runs the C preprocessor alone and writes the preprocessor output to `lines` — not an executable. There was a question in January ([How to get XCode 8 C preprocessor to ignore // comments in defines](https://stackoverflow.com/questions/41603784/)) where it turned out the Apple `cpp` command didn't behave quite the same as the preprocessor invoked via Clang (system GCC — I also have a home-built GCC on my machines). I wonder what you're thinking should happen with the `cpp` line? Why are you saving the preprocessed output in the logical name for the binary? – Jonathan Leffler Feb 03 '17 at 20:15
  • @JonathanLeffer, I was just playing around when saving the output of the preprocessor to see the type of output generated by the preprocessor. I am just getting familiar with the tools. My point here is why gcc -E and cpp behave differently when conceptually they should do the same: my library should either contain errors or not but why I get different outcomes? – MartiSanchez Feb 03 '17 at 20:35
  • @JonathanLeffer reading your comment I have notice Iwas runing Xcode 7.3 and Command_Line_Tools_OS_X_10.11_for_Xcode_7.3. I am now upgrading. I almost prefer this not to solve the issue to be able to find the original error... I will post an update when I run the commands with the updated tools – MartiSanchez Feb 03 '17 at 20:36
  • 2
    `cc -E` is the correct way to see the preprocessor output for a C program. `cpp` is also a C preprocessor but it has a different set of predefined macros, active extensions, etc. because nowadays its primary function is to preprocess *things that are not C*, e.g. FORTRAN code and X resources files. (I am not making this up.) That difference is probably sufficient to explain the problem you are having. – zwol Feb 03 '17 at 20:40
  • @user2353698: Just as an FYI, I didn't get notified of your comments, presumably because you missed the second ell in my name. – Jonathan Leffler Feb 03 '17 at 21:30
  • The good news: my copy of AvailabilityInternal.h has a `#else` at line 20265 and a `#if` as quoted at 20832, so it superficially looks like the same file. The bad news: the `#else` has a `#if` before it at line 20258 (`#if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_2`), and the `#if` at 20832 has a `#else` at 20867 and a `#endif` at 20902. Please create an MCVE ([MCVE]) which demonstrates the problem, showing what, if any, `#define` operations you've set before the compilation. You've got to be trying fairly hard to run into this, I think, but I'm not even sure how you could break it. – Jonathan Leffler Feb 03 '17 at 21:36
  • Since the header that triggers the problem is `` and this is the first header in your file `lines.c`, you should be able to make a copy of `lines.c`, then edit `lines.c` to include two lines: `#include ` and `int main(void) { puts("Hi"); return 0; }`, and compiling it in the same way should trigger the same error. If it doesn't, we've got some serious discussing to do. If it does, you've got your MCVE code. Do you run into the problem if you omit the `-I/opt/local/libpng-1.6.24/include` option? If you omit the `-std=c99` option? – Jonathan Leffler Feb 03 '17 at 21:40
  • I have reproduced the problem with the two lines of C code (you could use just one, of course — simply the include line — but you'd not generate anything when the compilation works). I get 20-odd errors. Since the `cpp` doesn't work, don't use it ("Doctor, doctor, it hurts when I do this"; "Then stop doing it"). The C preprocessor (`cpp`) that comes with GCC 6.3.0 has no problem with the header. It is probably, therefore, a bug in the standalone `cpp` program distributed with XCode. But it is not clear why it fails. Using the `-v` option to `/usr/bin/gcc` or `/usr/bin/cpp` doesn't help. – Jonathan Leffler Feb 03 '17 at 21:54

2 Answers2

2

As noted in the comments, I can reproduce your problem on my macOS Sierra 10.12.3 machine with XCode 8, getting (at least) 20 errors so it stops on the 20th.

Source file:

#include <stdio.h>
int main(void) { puts("Hi"); return 0; }

Command line and error output:

$ /usr/bin/cpp -v min.c -o min.c.out >/dev/null
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin16.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.12.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -E -disable-free -disable-llvm-verifier -discard-value-names -main-file-name min.c -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -masm-verbose -munwind-tables -target-cpu penryn -target-linker-version 274.2 -v -dwarf-column-info -debugger-tuning=lldb -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.0.0 -fdebug-compilation-dir /Users/jleffler/soq -ferror-limit 19 -fmessage-length 110 -stack-protector 1 -fblocks -fobjc-runtime=macosx-10.12.0 -fencode-extended-block-signature -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -traditional-cpp -o - -x c min.c
clang -cc1 version 8.0.0 (clang-800.0.42.1) default target x86_64-apple-darwin16.4.0
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.0.0/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
 /usr/include
 /System/Library/Frameworks (framework directory)
 /Library/Frameworks (framework directory)
End of search list.
In file included from min.c:1:
In file included from /usr/include/stdio.h:65:
In file included from /usr/include/Availability.h:184:
/usr/include/AvailabilityInternal.h:20265:10: error: #else without #if
        #else
         ^
/usr/include/AvailabilityInternal.h:20832:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_11_4
         ^
/usr/include/AvailabilityInternal.h:20765:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_11_3
         ^
/usr/include/AvailabilityInternal.h:20702:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_11_2
         ^
/usr/include/AvailabilityInternal.h:20643:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_11
         ^
/usr/include/AvailabilityInternal.h:20588:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10_3
         ^
/usr/include/AvailabilityInternal.h:20537:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10_2
         ^
/usr/include/AvailabilityInternal.h:20490:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10
         ^
/usr/include/AvailabilityInternal.h:20447:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_9
         ^
/usr/include/AvailabilityInternal.h:20408:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_8
         ^
/usr/include/AvailabilityInternal.h:20373:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_7
         ^
/usr/include/AvailabilityInternal.h:20342:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_6
         ^
/usr/include/AvailabilityInternal.h:20315:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_5
         ^
/usr/include/AvailabilityInternal.h:20292:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_4
         ^
/usr/include/AvailabilityInternal.h:20273:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_3
         ^
In file included from min.c:1:
In file included from /usr/include/stdio.h:65:
/usr/include/Availability.h:233:2: error: #else without #if
#else
 ^
/usr/include/Availability.h:236:2: error: #endif without #if
#endif
 ^
/usr/include/Availability.h:272:2: error: #endif without #if
#endif
 ^
/usr/include/Availability.h:299:2: error: #endif without #if
#endif
 ^
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.
$

Note that the only options I used were -v (to produce the verbose output at the start) and -o min.c.out (which is over-ridden with -o - in the verbose output; it did as it wanted rather than as I told it to do — rank insubordination, really). The redirection of standard output to /dev/null was necessary to avoid seeing all the output, therefore.

If I replace /usr/bin/cpp with /usr/bin/gcc (the path is necessary for me since gcc is /opt/gcc/v6.2.0/bin/gcc, aka GCC 6.2.0), and I add the -E option, it works fine:

$ /usr/bin/gcc -E -v min.c -o min.c.out
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin16.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.12.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -E -disable-free -disable-llvm-verifier -discard-value-names -main-file-name min.c -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -masm-verbose -munwind-tables -target-cpu penryn -target-linker-version 274.2 -v -dwarf-column-info -debugger-tuning=lldb -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.0.0 -fdebug-compilation-dir /Users/jleffler/soq -ferror-limit 19 -fmessage-length 110 -stack-protector 1 -fblocks -fobjc-runtime=macosx-10.12.0 -fencode-extended-block-signature -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -o min.c.out -x c min.c
clang -cc1 version 8.0.0 (clang-800.0.42.1) default target x86_64-apple-darwin16.4.0
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.0.0/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
 /usr/include
 /System/Library/Frameworks (framework directory)
 /Library/Frameworks (framework directory)
End of search list.
$ wc -l min.c.out
     456 min.c.out
$

This strongly hints at a problem of some sort in the /usr/bin/cpp program and the way it drives the rest of the compilation tool chain, but I've not identified the crucial difference in the command line.

It also hints at a solution — don't use cpp standalone. As I noted in comments, there was a question from January 2017 — How to get XCode 8 C preprocessor to ignore // comments in #defines? that also pointed to a problem in the standalone cpp.

If you must have a working cpp, then install your own. For example, you could install GCC 6.3.0 (I've been lazy on this machine; my other one is already running 6.3.0, not 6.2.0) in some location (e.g. /opt/gcc/v6.3.0/bin/cpp) and then arrange for that to be run instead of /usr/bin/cpp when you type cpp — adjust PATH, or put a symlink or script in a directory such as /usr/local/bin or $HOME/bin that occurs on PATH before /usr/bin, or specify the correct PATH in the makefile, or whatever it takes.

If you want guidelines on building GCC on a Mac, consider:

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    The `cpp` invocation passes `-traditional-cpp` to the internal clang binary, and the `gcc` invocation does not. That is almost certainly intentional, and it is also almost certainly the cause of the problem. ("traditional" in this context means "pre-C89" and could very easily break system headers, e.g. by not supporting token pasting or indented preprocessor directives.) – zwol Feb 04 '17 at 15:46
  • 1
    I have now reproduced the problem for the min.c file proposed by @JonathanLeffler. I get the same error. But when I copy-paste the clang command+options that `cpp -v...` generates and remove the option `traditional-cpp` and replace `-o -` by `-o min.c.out` all works with no errors and generates an output that at least in number of lines and words is the same we were getting with gcc. So as @zwol pointed out the traditional mode is the reason. Why cpp is translated to use `traditional-cpp` we do not know – MartiSanchez Feb 04 '17 at 21:47
1

I don't appear to have exactly the same AvailabilityInternal.h on my Mac as either user2353698 or Jonathan Leffler does, but I can still reproduce the problem:

$ /usr/bin/cpp -w /usr/include/AvailabilityInternal.h > /dev/null
/usr/include/AvailabilityInternal.h:14923:10: error: #else without #if
        #else
         ^

With some help from the insanely handy delta utility I have cut it down to a minimized test case:

#ifndef foo
    #ifdef bar
    #endif
#endif

This is perfectly legal standard C, but the traditional (K&R) preprocessor would not have understood either of the indented lines as preprocessing directives. clang's emulation of traditional preprocessing mode appears to have a bug, in which the indented #ifdef is not processed but the indented #endif is processed, so the subsequent non-indented #endif appears to be unbalanced. (Compare the behavior of gcc -E -traditional-cpp on this file, on a computer where gcc is actually GCC.) This has now been reported as LLVM bug #31886.

What I said in comments yesterday is still true: if you want to preprocess standard C, use cc -E. Even without this bug, cpp would not give the correct result on these system headers. It is intentional that cpp uses traditional mode, because the primary function of that command line tool nowadays is to preprocess things that are not C, such as FORTRAN, Makefiles, and X resources files. The traditional preprocessor is better suited to this task, as it is not as specialized for C's lexical syntax.

zwol
  • 135,547
  • 38
  • 252
  • 361