Подтвердить что ты не робот

Gcc/g++ для размещения всех объектных файлов в отдельном каталоге

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

Например:

mkdir builddir
mkdir builddir/objdir
cd srcdir

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

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

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

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

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

Это вырезанный файл makefile для одного из моих проектов, который компилирует источники в '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 [email protected] $< $(CFLAGS) 

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

.PHONY: clean

clean:
    rm -f $(ODIR)/*.o $(OUT)

Ответ 2

Как насчет перехода в каталог и выполнения компиляции отсюда:

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

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

Ответ 3

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

mv *.o ../builddir/objdir

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

rm -f *.o

или

find . -name \*.o -exec rm {} \;

Ответ 4

Вы можете использовать простую оболочку вокруг 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 script в его простейшей форме:

#!/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, чтобы включить новый параметр, но это может быть сложно.

Ответ 5

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

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

Ваш пример:

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 .)

Ответ 6

Я думаю, что передача 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 и считаю его очень удобным. Он автоматически генерирует зависимости и имеет другие полезные свойства.

Ответ 7

Между тем я нашел решение "на полпути", используя опцию -combine.

Пример:

mkdir builddir
mkdir builddir/objdir
cd srcdir

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

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

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

Ответ 8

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

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

configure script создает Makefiles в каталоге сборки. Затем происходит весь процесс сборки. Таким образом, все файлы сборки, естественно, появляются там, а не в исходном дереве.

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

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

Ответ 9

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

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
''

Ответ 10

Лично для отдельных файлов я делаю это,

rm -rf temps; mkdir temps; cd temps/ ; gcc -Wall -v --save-temps  ../thisfile.c ; cd ../ ; geany thisfile.c temps/thisfile.s temps/thisfile.i
В

папке temps будут храниться все файлы объектов, предварительно обработанные и сборки.

Это грубый способ сделать что-то, и я бы предпочел ответы выше, используя Makefiles.