Variáveis condicionais no Makefile

1

Estou tentando compilar o código-fonte de várias maneiras. Por exemplo, suponha que eu queira compilar um programa fortran com o código-fonte main.f90 , file1.f90 e file2.f90 como entradas. Para gerar hello.exe , eu simplesmente faria

FC=gfortran
EXE=hello.exe
OBJS=file1.o file2.o
FFLAGS=

$(EXE): $(OBJS)
    $(FC) $(FFLAGS) main.f90 $(OBJS) -o $(EXE)

file1.o: file1.f90
    $(FC) $(FFLAGS) -c file1.f90

file2.o: file2.f90
    $(FC) $(FFLAGS) -c file2.f90

clean:
    rm -rf *.o

Agora, suponha que eu queira fazer várias versões disso e comparar a saída. Eu gostaria de fazer um hello_O2.exe que usa o sinalizador de compilação -O2 , uma versão hello_O3.exe com um sinal -O3 e até compilar com compiladores diferentes. Eu gostaria de um hello_intel_O2.exe que usa o -O2 sinalizador, bem como usa o compilador ifort intel, por exemplo.

Idealmente, gostaria de especificar make intelO3 para usar o compilador ifort e -O3 que geraria o executável hello_intel_O3.exe . Se eu especificar make all , ele criaria os binários hello_O2.exe , hello_O3.exe , hello_intel_O2.exe e hello_intel_O3.exe , cada um com os níveis corretos de compilador e otimização.

Como isso pode ser alcançado neste exemplo mínimo? Meu Makefile atual tem ~ 120 linhas agora e usa muitas variáveis que eu mudo cada vez que quero construir para uma versão diferente.

    
por drjrm3 02.08.2015 / 18:00

2 respostas

0

Você pode simplesmente sobrescrever qualquer variável definida no makefile quando dê-lhes como argumentos para o comando make. Então, por exemplo:

#!/bin/make
FC=gfortran
EXE=hello$(SUFFIX).exe
OBJS=file1.o file2.o
FFLAGS=

default: $(EXE)

intelO3:
        $(MAKE) FC=ifort FFLAGS=-O3 SUFFIX=_intel_O3
all:
        $(MAKE) FFLAGS=-O2 SUFFIX=_O2
        $(MAKE) FFLAGS=-O3 SUFFIX=_O3
        $(MAKE) FC=ifort FFLAGS=-O3 SUFFIX=_intel_O3

... rest of makefile
    
por 02.08.2015 / 18:59
0
  1. Sugiro que, assim como você deseja várias cópias diferentes do arquivo de programa .exe , também mantém várias cópias dos arquivos .o . Algo parecido com isto:

    file1_gfortran.o: file1.f90
            gfortran $(FFLAGS) -c file1.f90 -o file1_gfortran.o
    
    file2_gfortran.o: file2.f90
            gfortran $(FFLAGS) -c file2.f90 -o file2_gfortran.o
    
    file1_ifort.o:    file1.f90
            ifort    $(FFLAGS) -c file1.f90 -o file1_ifort.o
    
    file2_ifort.o:    file2.f90
            ifort    $(FFLAGS) -c file2.f90 -o file2_ifort.o
    

    caso contrário, você precisará recompilar todos os % arquivos.f90 sempre que você modificar algum deles. (Isso será significativo quando você tiver file3.f90 , file4.f90 , etc ...)

    • Você pode eliminar parte da redundância detalhada; por exemplo, use -o $< . Veja também o parágrafo 5 abaixo.
    • Talvez seja necessário ter duas cópias diferentes de FFLAGS , se houver mais do que apenas a bandeira -On .
    • Eu acho que você vai querer definir OBJS=file1_$(FC).o file2_$(FC).o .
  2. Em vez de apenas ter uma lista de comandos, sugiro que você identifique explicitamente todos os arquivos de destino que você (talvez) queira produzir; por exemplo,

    hello.exe:
            $(MAKE)
    
    hello_O2.exe:
            $(MAKE)          FFLAGS=-O2 SUFFIX=_O2
    
    hello_O3.exe:
            $(MAKE)          FFLAGS=-O3 SUFFIX=_O3
    
    hello_intel.exe:
            $(MAKE) FC=ifort            SUFFIX=_intel
    
    hello_intel_O2.exe:
            $(MAKE) FC=ifort FFLAGS=-O2 SUFFIX=_intel_O2
    
    hello_intel_O3.exe:
            $(MAKE) FC=ifort FFLAGS=-O3 SUFFIX=_intel_O3
    

    então você pode dizer

    intelO3: hello_intel_O3.exe
    

    sem ter que inserir o $(MAKE) FC=ifort FFLAGS=-O3 SUFFIX=_intel_O3 comando duas vezes. Então, obviamente, você diria:

    all: hello.exe hello_O2.exe hello_O3.exe hello_intel.exe hello_intel_O2.exe hello_intel_O3.exe
    
  3. Na verdade, o acima provavelmente funcionaria melhor com pseudo-alvos:

    all: gf gfO2 gfO3 intel intelO2 intelO3
    
    gf:
            $(MAKE) FC=gfortran
    
    gfO2:
            $(MAKE) FC=gfortran FFLAGS=-O2 SUFFIX=_O2
    
    gfO3:
            $(MAKE) FC=gfortran FFLAGS=-O3 SUFFIX=_O3
    
    intel:
            $(MAKE) FC=ifort               SUFFIX=_intel
    
    intelO2:
            $(MAKE) FC=ifort    FFLAGS=-O2 SUFFIX=_intel_O2
    
    intelO3:
            $(MAKE) FC=ifort    FFLAGS=-O3 SUFFIX=_intel_O3
    
  4. E então você pode ser capaz de recolher as últimas seis estrofes acima para algo como :

    gf gfO2 gfO3 intel intelO2 intelO3:
            case $< in (gf*) compiler=gfortran; suffix="";; \
                       (in*) compiler=ifort; suffix="_intel";; esac; \
            case $< in (*O2) flags="-O2"; suffix="${suffix}_O2";; \
                       (*O3) flags="-O3"; suffix="${suffix}_O3";; esac; \
            $(MAKE) FC="$compiler" FFLAGS="$flags" SUFFIX="$suffix"
    

    Note que este é um comando shell multi-linha (continuado por barras invertidas nas extremidades das linhas). Você precisa disso se quiser definir uma variável shell em uma linha e usá-lo no próximo; o comportamento padrão de make é gerar um processo shell separado para cada linha de comando, perdendo assim efeitos secundários locais como o diretório de trabalho ( cd ) e variáveis de shell.

  5. Voltando ao primeiro parágrafo: você pode simplificar isso para

    file1_gfortran.o file1_ifort.o: file1.f90
            case $< in (_gf*) compiler=gfortran;; \
                       (_if*) compiler=ifort;; esac; \
            "$compiler" $(FFLAGS) -c file1.f90 -o $<
    
    file2_gfortran.o file2_ifort.o: file2.f90
            case $< in (_gf*) compiler=gfortran;; \
                       (_if*) compiler=ifort;; esac; \
            "$compiler" $(FFLAGS) -c file2.f90 -o $<
    

    e eu suspeito que você pode combinar o acima em uma única estrofe (começando com algo como X_gfortran.o X_ifort.o: X.f90 ), mas não me lembro como.

Advertência: Eu não testei nada disso.

    
por 02.08.2015 / 21:24