опция gcc / g ++ для размещения всех объектных файлов в отдельном каталоге - PullRequest
57 голосов
/ 29 ноября 2009

Мне интересно, почему gcc / g ++ не имеет возможности поместить сгенерированные объектные файлы в указанную директорию.

Например:

mkdir builddir
mkdir builddir/objdir
cd srcdir

gcc -c file1.c file2.c file3.c **--outdir=**../builddir/objdir

Я знаю, что этого можно добиться с помощью отдельных опций -o, предоставленных компилятору, например ::100100

gcc -c file1.c -o ../builddir/objdir/file1.o
gcc -c file2.c -o ../builddir/objdir/file2.o
gcc -c file3.c -o ../builddir/objdir/file3.o

... и я знаю, что могу написать Makefile через директивы VPATH и vpath, чтобы упростить это.

Но это сложная работа в сложной среде сборки.

Я мог бы также использовать

gcc -c file1.c file2.c file3.c

Но когда я использую этот подход, мой srcdir полон мусора .o впоследствии.

Так что я думаю, что вариант с семантикой --outdir был бы очень полезен.

Каково ваше мнение?

EDIT : наши Make-файлы написаны таким образом, что файлы .o фактически помещаются в builddir / obj. Но мне просто интересно, может ли быть лучший подход.

РЕДАКТИРОВАТЬ : Есть несколько подходов, которые возлагают бремя на достижение желаемого поведения в системе сборки (например, Make, CMake и т. Д.). Но я считаю, что все они обходные пути из-за слабости gcc (и других компиляторов тоже).

Ответы [ 9 ]

65 голосов
/ 12 декабря 2009

Это урезанный make-файл для одного из моих проектов, который компилирует исходники в 'src' и помещает файлы .o в каталог "obj". Ключевым битом является использование функции patsubst () - подробности см. В руководстве по сборке GNU (которое на самом деле очень хорошо читается):

OUT = lib/alib.a
CC = g++
ODIR = obj
SDIR = src
INC = -Iinc

_OBJS = a_chsrc.o a_csv.o a_enc.o a_env.o a_except.o \
        a_date.o a_range.o a_opsys.o
OBJS = $(patsubst %,$(ODIR)/%,$(_OBJS))


$(ODIR)/%.o: $(SDIR)/%.cpp 
    $(CC) -c $(INC) -o $@ $< $(CFLAGS) 

$(OUT): $(OBJS) 
    ar rvs $(OUT) $^

.PHONY: clean

clean:
    rm -f $(ODIR)/*.o $(OUT)
16 голосов
/ 29 ноября 2009

Как насчет того, чтобы перейти в каталог и запустить компиляцию оттуда:

cd builddir/objdir
gcc ../../srcdir/file1.c ../../srcdir/file2.c ../../srcdir/file3.c

Вот и все. gcc будет интерпретировать включения в форме #include "path/to/header.h", начиная с каталога, в котором существует файл, поэтому вам не нужно ничего изменять.

14 голосов
/ 12 декабря 2009

Тривиальный, но эффективный обходной путь - добавить следующее сразу после вызова gcc в вашем Makefile:

mv *.o ../builddir/objdir

или даже soft-clean (возможно, рекурсивный) после завершения компиляции, как

rm -f *.o

или

find . -name \*.o -exec rm {} \;
10 голосов
/ 16 декабря 2009

Вы можете использовать простую оболочку вокруг gcc, которая сгенерирует необходимые параметры -o и вызовет gcc:

$ ./gcc-wrap -c file1.c file2.c file3.c --outdir=obj 
gcc -o obj/file1.o -c file1.c
gcc -o obj/file2.o -c file2.c
gcc -o obj/file3.o -c file3.c

Вот такой gcc_wrap скрипт в простейшем виде:

#!/usr/bin/perl -w

use File::Spec;
use File::Basename;
use Getopt::Long;
Getopt::Long::Configure(pass_through);

my $GCC = "gcc";
my $outdir = ".";
GetOptions("outdir=s" => \$outdir)
    or die("Options error");

my @c_files;
while(-f $ARGV[-1]){
    push @c_files, pop @ARGV;
}
die("No input files") if(scalar @c_files == 0);

foreach my $c_file (reverse @c_files){
    my($filename, $c_path, $suffix) = fileparse($c_file, ".c");
    my $o_file = File::Spec->catfile($outdir, "$filename.o");
    my $cmd = "$GCC -o $o_file @ARGV $c_file";
    print STDERR "$cmd\n";
    system($cmd) == 0 or die("Could not execute $cmd: $!");
}

Конечно, стандартным способом является решение проблемы с Makefiles, или проще, с CMake или bakefile, но вы специально попросили решение, которое добавляет функциональность к gcc, и я думаю, единственный способ - написать такую ​​обертку. Конечно, вы могли бы также пропатчить источники gcc, чтобы включить новую опцию, но это может быть сложно.

4 голосов
/ 15 декабря 2009

Полагаю, вы поняли концепцию задом наперед ...?!

Идея Makefiles заключается в том, что они обрабатывают только файлы, которые были обновлены с момента последней сборки, чтобы сократить (пере) время компиляции. Если вы соберете вместе несколько файлов за один прогон компилятора, вы в основном победите эту цель.

Ваш пример:

gcc -c file1.c file2.c file3.c **--outdir=**../builddir/objdir

Вы не дали правило 'make', соответствующее этой командной строке; но если любой из трех файлов был обновлен, вы должны запустить эту строку и перекомпилировать все три файла, которые могут не понадобиться вообще. Он также не позволяет 'make' порождать отдельный процесс компиляции для каждого исходного файла, как это было бы для отдельной компиляции (при использовании опции '-j', как я настоятельно рекомендую).

Я написал учебник Makefile в другом месте, в котором рассматриваются некоторые дополнительные детали (например, автоматическое определение ваших исходных файлов вместо их жесткого кодирования в Makefile, автоматическое определение зависимостей включения и встроенное тестирование).

Все, что вам нужно сделать, чтобы получить отдельный каталог объектов, - это добавить соответствующую информацию о каталоге в строку OBJFILES := и правило %.o: %.c Makefile из этого руководства. В ответе Нила Баттерворта есть хороший пример того, как добавить информацию в каталог.

(Если вы хотите использовать DEPFILES или TESTFILES, как описано в руководстве, вам придется адаптировать строки DEPFILES := и TSTFILES := плюс правило %.t: %.c Makefile pdclib.a .) *

1 голос
/ 12 декабря 2009

Тем временем я нашел решение «на полпути», используя опцию -combine.

Пример:

mkdir builddir
mkdir builddir/objdir
cd srcdir

gcc -combine -c file1.c file2.c file3.c -o ../builddir/objdir/all-in-one.o

это «объединяет» все исходные файлы в один объектный файл.

Однако это все еще «наполовину», потому что нужно перекомпилировать все, когда изменяется только один исходный файл.

1 голос
/ 11 декабря 2009

Я думаю, что для передачи pass gcc нет отдельной опции, чтобы сказать, куда поместить объектный файл, так как он уже есть. Это «-c» - говорит, в какой каталог поместить объект.

Наличие дополнительного флага только для каталога должно изменить значение «-c». Например:

gcc -c file.c -o /a/b/c/file.o --put-object-in-dir-non-existing-option /a1/a2/a3

Вы не можете поместить /a/b/c/file.o в / a1 / a2 / a3, так как оба пути являются абсолютными. Таким образом, «-c» следует изменить только на имя объектного файла.

Советую подумать о замене make-файла, например cmake , scons и других. Это позволит реализовать систему сборки как для простого проекта, так и для более крупного.

Посмотрите, например, как легко скомпилировать cmake на вашем примере. Просто создайте файл CMakeList.txt в srcdir /:

cmake_minimum_required(VERSION 2.6)
project(test) 

add_library(test file1.c file2c file3.c) 

А теперь наберите:

mkdir -p builddir/objdir
cd builddir/objdir
cmake ../../srcdir
make

Вот и все, объектные файлы будут находиться где-то в builddir / objdir.

Лично я использую cmake и нахожу его очень удобным. Он автоматически генерирует зависимости и имеет другие плюсы.

0 голосов
/ 11 марта 2019

Я пытаюсь понять то же самое. Для меня это сработало

CC = g++
CFLAGS = -g -Wall -Iinclude
CV4LIBS = `pkg-config --libs opencv4`
CV4FLAGS = `pkg-config --cflags opencv4`

default: track

track:  main.o
    $(CC) -o track $(CV4LIBS) ./obj/main.o

ALLFLAGS = $(CFLAGS) $(CV4FLAGS)
main.o: ./src/main.cpp ./include/main.hpp
    $(CC) $(ALLFLAGS) -c ./src/main.cpp $(CV4LIBS) -o ./obj/main.o
``
0 голосов
/ 16 декабря 2009

Это одна из проблем autoconf решает.

Если вы когда-либо выполняли ./configure && make, вы знаете, что такое autoconf: это инструмент, который генерирует эти приятные скрипты configure. Не все знают, что вы можете вместо этого сделать mkdir mybuild && cd mybuild && ../configure && make, и это будет работать волшебным образом, потому что autoconf - это круто.

Сценарий configure создает файлы Makefile в каталоге сборки . Тогда весь процесс сборки происходит там. Поэтому все файлы сборки появляются там, а не в дереве исходных текстов.

Если у вас есть исходные файлы, выполняющие #include "../banana/peel.h", и вы не можете их изменить, то будет затруднительно заставить эту работу работать правильно (вы должны скопировать или скомпоновать все заголовочные файлы в каталог сборки). Если вы можете изменить исходные файлы на #include "libfood/comedy/banana/peel.h", то все готово.

autoconf не совсем easy , особенно для большого существующего проекта. Но у него есть свои преимущества.

...