gcc / g ++ opção para colocar todos os arquivos objeto em diretório separado

StackOverflow https://stackoverflow.com/questions/1814270

  •  06-07-2019
  •  | 
  •  

Pergunta

Eu estou querendo saber porque gcc / g ++ não tem uma opção para colocar os arquivos objeto gerados em um diretório especificado.

Por exemplo:

mkdir builddir
mkdir builddir/objdir
cd srcdir

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

Eu sei que é possível para obter este com -o opções separadas dadas ao compilador, p.ex.:.

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

... e eu sei que eu posso escrever Makefiles via VPATH e directivas vpath para simplificar isso.

Mas isso é um monte de trabalho em um ambiente de construção complexa.

Eu também poderia usar

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

Mas quando eu usar essa abordagem minha srcdir está cheio de lixo .o depois.

Então eu acho que uma opção com a semântica de --outdir seria muito útil.

Qual é a sua opinião?

Editar : nossos Makefiles são escritos de tal forma que .o arquivos realmente colocado em builddir / obj. Mas estou simplesmente querendo saber se poderia haver uma melhor abordagem.

Editar : Há várias abordagens que colocam a carga para atingir o comportamento desejado para o sistema de construção (aka Faça, CMake etc.). Mas eu considero todos eles como sendo soluções para uma fraqueza do gcc (e outros compiladores também).

Foi útil?

Solução

Este é o makefile cortada para um dos meus projetos, que compila as fontes em 'src' e coloca os arquivos .o no "obj" diretório. O bit chave é o uso da função patsubst () - veja a GNU make manual (que na verdade é uma boa leitura muito) para obter detalhes:

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)

Outras dicas

Que tal mudar para o diretório e executar a compilação de lá:

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

É isso. gcc irá interpretar inclui do #include "path/to/header.h" forma como começando no diretório o arquivo existe, assim você não precisa modificar nada.

Uma solução trivial, mas eficaz é adicionar o seguinte logo após a chamada gcc em seu Makefile:

mv *.o ../builddir/objdir

ou mesmo um soft-limpa (possivelmente recursiva) após a compilação é feita, como

rm -f *.o

ou

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

Você pode usar um invólucro simples em torno gcc que irá gerar as opções -o necessárias e gcc chamada:

$ ./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

Aqui está como roteiro uma gcc_wrap em sua forma mais simples:

#!/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: $!");
}

É claro, a forma padrão é resolver o problema com Makefiles, ou mais simples, com CMake ou bakefile, mas você pediu para uma solução que adiciona a funcionalidade para gcc, e acho que a única maneira é escrever um tal embrulho. Claro, você também poderia corrigir as fontes gcc para incluir a nova opção, mas isso pode ser difícil.

Eu acredito que você tem as costas conceito ...?!

A idéia por trás Makefiles é que eles só processar os arquivos que foram atualizados desde a última compilação, para reduzir os tempos de (re) compilação. Se você bando vários arquivos juntos em uma corrida compilador, você basicamente derrotar o efeito.

O seu exemplo:

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

Você não deu a regra 'make' que se passa com esta linha de comando; mas se qualquer dos três arquivos foi atualizado, você tem que executar esta linha, e recompilar todas as três arquivos, que pode não ser necessária. Ele também mantém 'make' da desova um processo de compilação separada para cada arquivo-fonte, como faria para a compilação separada (quando utilizar o '-j' opção, como eu sugeriria fortemente).

Eu escrevi um Makefile tutorial em outros lugares, que entra em algum detalhe extra (como auto-detecção seus arquivos de origem em vez de tê-los hard-coded no Makefile, auto-determinação incluem dependências, e testes de linha).

Tudo que você tem que fazer para obter o seu diretório objeto separado seria adicionar as informações do diretório apropriado para a linha OBJFILES := e do Estado %.o: %.c Makefile daquele tutorial. A resposta de Neil Butterworth tem um bom exemplo de como adicionar as informações do diretório.

(Se você quiser usar DEPFILES ou TestFiles conforme descrito no tutorial, você tem que se adaptar as linhas DEPFILES := e TSTFILES := mais a regra %.t: %.c Makefile pdclib.a , também.)

Eu acho que dizendo passagem gcc não tem uma opção separada para dizer onde colocar arquivo objeto, uma vez que já tem. É "-c" - diz em qual diretório para colocar objeto.

Tendo bandeira adicional para diretório somente deve mudar meening de "c". Por exemplo:

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

Você não pode colocar /a/b/c/file.o sob / A1 / A2 / A3, já que ambos os caminhos são absolutos. Assim, "c" deve ser alterado para o nome do arquivo objeto somente.

Eu aconselho você a considerar uma substituição de makefile, como cmake , scons e outros. Isto irá permitir a implementar sistema de construção como para para o projeto simples, bem como para o maior um também.

Veja por exemplo, como é fácil compilar usando cmake seu exemplo. Basta criar arquivo CMakeList.txt em srcdir /:

cmake_minimum_required(VERSION 2.6)
project(test) 

add_library(test file1.c file2c file3.c) 

E agora digite:

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

Isso é tudo, arquivos objeto irá residir em algum lugar sob builddir / objdir.

Eu pessoalmente uso cmake e achar que é muito conveniente. Ele gera automaticamente as dependências e tem outras guloseimas.

Enquanto isso eu encontrei uma solução "meio caminho" usando a opção Combine.

Exemplo:

mkdir builddir
mkdir builddir/objdir
cd srcdir

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

este "combina" Todos os arquivos de origem em um arquivo único objeto.

No entanto, isso ainda é "meio caminho" porque ele precisa recompilar tudo quando as alterações de arquivos apenas uma fonte.

Este é um dos problemas autoconf resolve.

Se você já fez ./configure && make você sabe o autoconf é: é a ferramenta que gera essas agradáveis ??scripts de configuração. O que nem todos sabem é que você pode fazer em vez mkdir mybuild && cd mybuild && ../configure && make e que irá magicamente trabalho, porque autoconf é incrível dessa forma.

O script configure gera Makefiles no diretório de construção . Em seguida, todo o processo de construção acontece lá. Assim, todos os arquivos de compilação naturalmente aparecer lá, não na árvore de origem.

Se você tem arquivos de origem fazendo #include "../banana/peel.h" e você não pode alterá-los, então é uma dor para fazer este trabalho direito (você tem que copiar ou symlink todos os arquivos de cabeçalho na pasta de compilação). Se você pode mudar os arquivos de origem para dizer #include "libfood/comedy/banana/peel.h" vez, então você está tudo definido.

autoconf não é exatamente fácil , especialmente para um grande projeto existente. Mas tem suas vantagens.

Eu estou tentando descobrir a mesma coisa. Para mim isso funcionou

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
``
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top