有没有办法检测,C文件直接编译成可执行文件?

时间:2016-10-16 00:40:37

标签: c compilation clang c-preprocessor

我想在一些C文件中包含一个测试main - 函数 - 允许将它们编译成独立的小程序来独立测试各种函数。

但我不希望这些多个main - 函数包含在真实(大)可执行文件/库中。

显然,我可以使用我自己的define,例如-DINCLUDE_TEST_MAIN,但我想到,铿锵可能已经在告诉我了。不知何故...

那么,当编译代码直接编译成可执行文件而不是生成目标文件时(使用-c标志),编译代码是否有任何方法可以检测?

解决方案不一定是通用的 - 我很确定,通用的解决方案不存在 - 我的主要编译器是clang ...

3 个答案:

答案 0 :(得分:4)

我没有看到比-DINCLUDE_TEST_MAIN更好的解决方案。可能你可以创建一些花哨的命令行,如果你不需要它会剥离主文件,但我认为-D是最好的方法。

我真的不明白你的意思»但我发现,铿锵可能已经在告诉我了。不知何故......« - 如果你害怕名字冲突,而不仅仅是取名,叮当绝对不会使用,如-DMIKHAIL_T_INCLUDE_TEST_MAIN;如果这不是你的意思,那么你应该澄清这一点。

或者,除stuff.c之外,你可以创建stuff.main.c并像这样测试编译:

gcc stuff.c stuff.main.c -o stuff.test

(有效地将主要文件移出。)

答案 1 :(得分:2)

您可以(但不应该)使用-Xlinker -zmuldefs选项。

我发布这个答案就像额外的信息一样,因为我认为这种方法不是目前为止首选的方法,但我发现它很有趣并且它与你提出的这个非常有趣的问题有很大关系。

这样链接器将忽略多个定义,它将采用它在第一个.c文件中找到的第一个定义。

我必须强调,虽然我认为不应该使用-zmuldefs,除非在极端情况下你知道自己在做什么而你没有更好的选择。

我认为上面提到的-Dxxx解决方案远远优于它,它还需要构建额外的参数(这里唯一有争议的小优点是你不需要通过使用-zmuldefs aproach来改变参数来铿锵获得一些透明度,但我不认为这实际上是任何优势,如下所述)。

当你看到一个宏时,你向程序员展示了明确的意图,以便稍后维护代码。与查找main()并在数千个文件中查找它相反,除了编译和调试时,知道它实际使用的位置。

同样通过使用-zmuldefs,您需要按照编写文件的顺序注意,因为它很重要。

那就是说,如果我有2个文件,那就说x1.c和x2.c,其中

x1.c是:

#include <stdio.h>

#include "x2.h"

int x();

int main() {
    x();
}

int x() {
    static int isRecursiveCall_ = 0;

    printf("Hello, World 1!\r\n");

    if (!isRecursiveCall_) {
        isRecursiveCall_ = 1;
        main();
        x3();
        isRecursiveCall_ = 0;
    }
    return 0;
}

和x2.c是:

#include <stdio.h>

#include "x2.h"

int main() {
    printf("Hello, World 2!\r\n");
    x3();
    return 0;
}

int x3() {
    printf("Hello, World 3!\r\n");
    return 0;
}

x2.h在哪里

#ifndef LINKTEST_X_H
#define LINKTEST_X_H

int main2();
int x2();
int x3();

#endif //LINKTEST_X_H

您可以致电clang x2.c -Xlinker -zmuldefs -o test.o./test.o将输出:

Hello, World 2!
Hello, Wolrd 3!

然后你可以拨打clang x1.c x2.c -Xlinker -zmuldefs -o prog.o./prog.o,结果是:

Hello, World 1!
Hello, World 1!
Hello, World 3!

根据需要。

同样,这里唯一的优点是不必知道何时使用-Dxxx参数,并且存在许多缺点。

在我的示例中,使用-Dxxx方法,您必须在代码中明确说明您将使用哪种函数。例如,如果我按如下方式更改x2.c:

#include <stdio.h>

#include "x2.h"

#ifdef _UNIT_TEST_ENABLED
int main() {
    printf("Hello, World 2!\r\n");
    x3();
    return 0;
}
#endif

int x3() {
    printf("Hello, World 3!\r\n");
    return 0;
}

然后,当我构建x2.c独立版本时,您需要调用clang x2.c -DUNIT_TEST_ENABLED -o test.o,然后在构建所有版本时,您只需调用clang x1.c x2.c -o prog.o

这使得意图在代码和构建脚本中都清晰,并且允许以任何顺序放置文件。好得多。如果您将单元测试分离到另一个文件中,则更好,如前面的答案中所述。

最后,必须知道显式构建参数并具有明确的#define或单独的文件,这是您首先想要避免的,实际上是最好的事情。有时候透明度是好的,有些则是邪恶的。

答案 2 :(得分:1)

.o文件只是一堆带有符号表的机器代码。他们几乎没有其他信息。

您可以做的是将它们链接到不同的配置中。在Makefile中:

OBJS = some_file.o other_file.o

test_prog: $(OBJS) test_main.o

real_prog: $(OBJS) real_main.o

all: test_prog real_prog

如果您使用make之外的其他内容,其他构建工具中也可以使用相同的功能。

现在,您正在重复使用相同的目标文件将不同的对象组合在一起。与编译相比,链接是一种相对较快的操作。

请注意,这不会为您节省任何磁盘空间。如果这是一个问题,我无法从你的问题中得知。

如果磁盘空间有问题,您可以使用古老的技巧,使用不同的名称对主可执行文件进行符号链接,然后查看argv [0]并根据名称更改程序的行为正在被召唤。