Прекомпилировать заголовки в make-файл автоматически - PullRequest
0 голосов
/ 04 июля 2018

Цель моего Makefile - в конце концов создать статическую библиотеку * .a из файлов Fortran77 и некоторых * .c's + * .h, в то время как определенная часть заголовков должна быть предварительно скомпилирована с помощью специального внутреннего прекомпилятора компании, который предоставляется через исполняемый файл, и все, что вам нужно передать, это путь + имя файла.

Давайте назовем прекомпилятор CPreComp.

Файлы, требующие прекомпиляции * _l.h.

Итак, я хочу сначала собрать все заголовки, которые мне нужны для предварительной компиляции, а затем передать его сценарию, который выполняет магию (переменные env blubb blubb) и вызывает прекомпилятор.

Вот, пожалуйста, мой Makefile:

SHELL=/usr/bin/bash
.SHELLFLAGS:= -ec

SOURCE_PATH = ./src
CPRECOMP = ./tools/cprecomp.exe
DO_CPreComp = $(SOURCE_PATH)/do_cprec
HDREXT = .h
PREC_HEADERS = $(foreach d, $(SOURCE_PATH), $(wildcard $(addprefix $(d)/*, $(HDREXT))))

.PHONY: all prereq

all:: \
      prereq \
      lib.a

prereq: chmod 777 $(DO_CPreComp)
        echo $(PREC_HEADERS) >> makefileTellMeWhatYouHaveSoFar.txt

lib.a: \
     obj/file1.o \
     obj/file2.o
     ar -r lib.a $?

obj/file1.o:

# do some fortran precompiling stuff here for a specific file

obj/file2.o: $(SOURCE_PATH)/*.h precomp_path/*.h $(SOURCE_PATH)/file2.c precomp_path/%_l.h
         cc -c -g file2.c

precomp_path/%_l.h : DatabaseForPreComp.txt

precomp_path/%_l.h : 
       $(foreach i , $(PREC_HEADERS) , $(DO_CPreComp) $(i) $(CPRECOMP);)

Итак, это мой Makefile, скрипт для DO_CPreComp выглядит следующим образом:

#!/bin/bash
filename="(basename "$1")"
dir="$(dirname "$1")"
cprecomptool="$2"

echo ${dir} ${filename} ${cprecomptool} >> scriptTellMeWhatYouKnow.txt     

"${cprecomptool}" "precomp_path/${filename}.1" >&cprecomp.err
cp "precomp_path/${filename}.1" "precomp_path/${filename}"

Итак, в соответствии с makefileTellMeWhatYouHaveSoFar.txt я собираю все заголовки, очевидно, также те, которые не указаны в _l.h. Здесь есть место для улучшений, но прекомпилятор достаточно умен, чтобы пропустить файлы, которые не подходят. Итак, makefileTellMeWhatYouHaveSoFar.txt выглядит так:

header1.h header2.h header2_l.h headerx_l.h headery_l.h headerz.h

Ошибка говорит мне:

path_to_here/do_cprec : line xy: $2: unbound variable
make[2]: *** [precomp_path/%_l.h] Error 1
make[1]: *** [lib.a] Error 2

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

Дополнительно: Если я не вызову сценарий во втором foreach, но позволю распечатать $(i) с помощью echo в другом файле, он будет пуст.

Возможно, я просто слишком слеп. И, пожалуйста, если вы сможете мне помочь, объясните мне это для глупых людей, чтобы в следующий раз я наткнулся на проблему, я стал умнее, потому что я знаю, что я делаю. :)

Ответы [ 2 ]

0 голосов
/ 04 июля 2018

ОК, теперь, когда основная проблема решена, давайте посмотрим на стили создания кода. Лучший способ добиться того, чего вы хотите, - это не совсем использовать foreach в рецептах. Этот подход имеет несколько недостатков, таких как, например, тот факт, что make не может выполнять параллельные задания, в то время как он чрезвычайно хорош в этом. А на современных многоядерных архитектурах это действительно может изменить ситуацию. Или тот факт, что вещи всегда переделаны, хотя они потенциально актуальны.

Предполагая, что результатом предварительной компиляции файла foo_l.h будет foo.h (мы рассмотрим другие варианты позже), путь создания будет выглядеть примерно так:

SOURCE_PATH         := ./src
CPRECOMP            := ./tools/cprecomp.exe
DO_CPreComp         := $(SOURCE_PATH)/do_cprec
HDREXT              := .h
PREC_HEADERS        := $(wildcard $(addsuffix /*_l.$(HDREXT),$(SOURCE_PATH)))
PRECOMPILED_HEADERS := $(patsubst %_l.h,%.h,$(PREC_HEADERS))

$(PRECOMPILED_HEADERS): %_l.h: %.h DatabaseForPreComp.txt
    $(DO_CPreComp) $@ $(CPRECOMP)

($@ расширяется как цель). Это статическое шаблонное правило . С этим стилем кодирования перестраиваются только заголовки, которые должны быть предварительно скомпилированы (потому что они старше, чем их предварительные условия). И если вы запустите make в параллельном режиме (make -j4 для 4 параллельных заданий), вы увидите хороший коэффициент ускорения для многоядерного процессора.

Но что, если прекомпиляция изменяет сам файл foo_l.h? В этом случае вам понадобится другой фиктивный (пустой) файл, чтобы отслеживать, когда файл был предварительно скомпилирован:

SOURCE_PATH         := ./src
CPRECOMP            := ./tools/cprecomp.exe
DO_CPreComp         := $(SOURCE_PATH)/do_cprec
HDREXT              := .h
PREC_HEADERS        := $(wildcard $(addsuffix /*_l.$(HDREXT),$(SOURCE_PATH)))
PREC_TAGS           := $(patsubst %,%.done,$(PREC_HEADERS))

$(PREC_TAGS): %.done: % DatabaseForPreComp.txt
    $(DO_CPreComp) $< $(CPRECOMP) && \
    touch $@

($< расширяется как первая предпосылка). Хитрость в том, что пустой файл foo_l.h.done является маркером. Его время последнего изменения записывает последний раз, когда foo_l.h был предварительно скомпилирован. Если с тех пор foo_l.h или DatabaseForPreComp.txt изменилось, то foo_l.h.done устарело и make перестраивает его, то есть предварительно компилирует foo_l.h, а затем нажимает foo_l.h.done, чтобы обновить время последнего изменения. Конечно, если вы используете это, вы должны указать make, что некоторые другие цели зависят от $(PREC_TAGS).

0 голосов
/ 04 июля 2018

С помощью @Renaud Pacalet мне удалось найти решение. В комментариях вы можете прочитать дальше try & errors.

Я использую GNU Make 3.82, созданную для x86_64-redhat-linux-gnu. Похоже, что foreach не нравится пробел за i или, кроме того, занимает пробел как часть переменной.

# ... like beforehand check out in the question
PREC_HEADERS=$(shell find $(SOURCE_PATH) -iname '*_l.h')
# nothing changed here in between...
$(foreach i,$(PREC_HEADERS),$(DO_CPC) $i $(CPC);)

Это имеет то преимущество, что я прекомпилирую только заголовки, которые имеют _l.h - окончание. Наличие brackets $(i) вокруг $i или нет, не вносит изменений. Что действительно изменило все, так это пространство позади первого i.

Удачи!

...