1

Я пытаюсь скомпилировать исходный код различными способами. Например, предположим, что я хочу скомпилировать фортран-программу с исходным кодом main.f90 , file1.f90 и file2.f90 качестве входных данных. Чтобы сгенерировать hello.exe , я бы просто сделал

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

Теперь предположим, что я хочу сделать разные версии этого и сравнить результаты. Я хотел бы создать hello_O2.exe который использует флаг компиляции -O2 , версию hello_O3.exe с флагом -O3 и даже компилировать с различными компиляторами. Я хотел бы hello_intel_O2.exe который использует флаг -O2 а также использует компилятор Intel ifort , например.

В идеале я бы хотел указать make intelO3 для использования компилятора ifort и флага -O3 который и сгенерирует исполняемый файл hello_intel_O3.exe . Если я указываю , make all это создало бы бинарные файлы hello_O2.exe hello_O3.exe hello_intel_O2.exe и hello_intel_O3.exe каждый с правильным уровнем компилятора и оптимизации.

Как это может быть достигнуто в этом минимальном примере? Мой настоящий Makefile сейчас ~ 120 строк и использует много переменных, которые я меняю каждый раз, когда хочу построить для другой версии.

2 ответа2

0
  1. Я полагаю, что так же, как вы хотите иметь несколько разных копий программного файла .exe , вы также должны поддерживать несколько копий файлов .o . Что-то вроде этого:

    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
    

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

    • Вы можете быть в состоянии устранить некоторые подробные избыточности; например, используйте -o $< .  Смотрите также пункт 5 ниже.
    • Вам может понадобиться две разные копии FFLAGS , если там больше, чем просто флаг -On .
    • Я предполагаю, что вы захотите определить OBJS=file1_$(FC).o file2_$(FC).o .
  2. Вместо того, чтобы просто иметь список команд, я предлагаю вам явно указать все целевые файлы, которые вы (возможно) захотите создать; например,

    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
    

    так что вы можете сказать

    intelO3: hello_intel_O3.exe
    

    без необходимости дважды вводить команду $(MAKE) FC=ifort FFLAGS=-O3 SUFFIX=_intel_O3 .  Тогда, очевидно, вы бы сказали:

    all: hello.exe hello_O2.exe hello_O3.exe hello_intel.exe hello_intel_O2.exe hello_intel_O3.exe
    
  3. На самом деле, вышеупомянутое, вероятно, будет работать лучше с псевдо-целями:

    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. И тогда вы сможете свернуть последние шесть строф выше примерно так :

    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"
    

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

  5. Возвращаясь к первому абзацу: вы можете упростить это до

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

    и я подозреваю, что вы можете объединить вышеперечисленное в единую строфу (начиная с чего-то вроде X_gfortran.o X_ifort.o: X.f90), но я не могу вспомнить, как.

Предостережение: я не проверял ничего из этого.

0

Вы можете просто переопределить любые переменные, установленные в make-файле, когда вы даете их в качестве аргументов для команды make. Так, например:

#!/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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .