bagaimana mencegah "direktori sudah ada kesalahan" di makefile saat menggunakan mkdir

109

Saya perlu membuat direktori di makefile saya dan saya tidak ingin "direktori sudah ada kesalahan" berulang-ulang meskipun saya dapat dengan mudah mengabaikannya.

Saya terutama menggunakan mingw / msys tetapi ingin sesuatu yang berfungsi di shell / sistem lain juga.

Saya mencoba ini tetapi tidak berhasil, ada ide?

ifeq (,$(findstring $(OBJDIR),$(wildcard $(OBJDIR) )))
-mkdir $(OBJDIR)
endif
KPexEA
sumber

Jawaban:

106

Di UNIX Cukup gunakan ini:

mkdir -p $(OBJDIR)

Opsi -p untuk mkdir mencegah pesan kesalahan jika direktori ada.

tchen
sumber
39
"Umumnya, tetap gunakan opsi dan fitur yang didukung secara luas (biasanya ditentukan posix) dari program ini. Misalnya, jangan gunakan 'mkdir -p', semudah mungkin, karena beberapa sistem tidak mendukungnya sama sekali dan dengan yang lain, ini tidak aman untuk eksekusi paralel. " gnu.org/s/hello/manual/make/Utilities-in-Makefiles.html
greg.kindel
8
-ptidak berfungsi pada Windows, yang mungkin ditanyakan oleh pertanyaan itu. Tidak yakin bagaimana ini bisa diterima.
Antimony
2
Untuk Windows, pilih ini: connect.microsoft.com/PowerShell/feedback/details/1074131/…
vulcan raven
1
@Antimony Anda mungkin melakukan kesalahan jika Anda menggunakan GNU Make di luar shell MinGW. Pilihanmu, meskipun.
yyny
"Di UNIX Cukup gunakan ini ..." - Apakah make -pUnix atau Linux?
jww
122

Melihat dokumentasi resmi make , berikut adalah cara yang baik untuk melakukannya:

OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)

$(OBJDIR)/%.o : %.c
    $(COMPILE.c) $(OUTPUT_OPTION) $<

all: $(OBJS)

$(OBJS): | $(OBJDIR)

$(OBJDIR):
    mkdir -p $(OBJDIR)

Anda harus melihat di sini penggunaan | operator pipa, hanya mendefinisikan prasyarat pesanan. Artinya $(OBJDIR)target harus ada (bukan yang lebih baru ) untuk membangun target saat ini.

Perhatikan bahwa saya dulu mkdir -p. The -pbendera ditambahkan dibandingkan dengan contoh dokumentasi. Lihat jawaban lain untuk alternatif lain.

ofavre
sumber
4
Ini jelas merupakan cara resmi untuk masalah ini.
lcltj
@CraigMcQueen: Saya benar-benar bermaksud " $(OBJDIR)target harus ada " . Lakukan pemeriksaan untuk keberadaan file (dan stempel waktu) untuk memutuskan apakah perlu membangun target. Oleh karena itu di sini, jika $(OBJDIR)tidak ada maka akan dibangun, sehingga ada untuk membangun target saat ini $(OBJS), yang akan dibuat di dalam.
ofavre
Saya pikir saya telah benar-benar mencoba setiap kombinasi lain untuk menyelesaikan masalah ini sebelum menemukan ini (saya mencoba menghasilkan makefile yang generik mungkin antara windows / linux) - ini adalah satu-satunya yang bisa saya kerjakan, tetapi itu bekerja dengan sangat baik! :)
code_fodder
67

Anda dapat menggunakan perintah tes:

test -d $(OBJDIR) || mkdir $(OBJDIR)
skymt
sumber
7
Ini adalah cara yang lebih disukai jika Anda membutuhkan makefile Anda untuk bekerja di Windows (dengan gnuwin32 dan PowerShell) dan OS yang sesuai dengan POSIX.
Kris Hardy
6
DIBERIKAN Anda memiliki paket gnuwin32 CoreUtils untuk menyediakan utilitas 'test'.
davenpcj
2
Jauh di sini, tetapi saya ingin menunjukkan bahwa solusi ini dapat rusak saat membangun secara paralel ( make -j) karena kondisi balapan antara saat direktori diuji keberadaan dan dibuat. Satu pekerjaan dapat menguji bahwa itu tidak ada, tetapi sebelum membuatnya, pekerjaan lain membuat direktori. Kemudian ketika yang pertama mencoba membuatnya akan gagal karena direktorinya sudah ada. Solusi terbaik dalam hal ini adalah menggunakan prasyarat hanya pesanan seperti yang disebutkan dalam jawaban @ TeKa (yang seharusnya merupakan jawaban yang diterima).
C0deH4cker
@MarcusJ Berfungsi di OS X El Capitan saya. Versi mana yang memecahkannya?
Franklin Yu
@ C0deH4cker - Maafkan ketidaktahuan saya ... Jawaban mana yang TeKa? Sepertinya TeKa mengubah namanya sejak Anda membuat komentar.
jww
18

Berikut adalah trik yang saya gunakan dengan GNU make untuk membuat direktori compiler-output. Pertama tentukan aturan ini:

  %/.d:
          mkdir -p $(@D)
          touch $@

Kemudian buat semua file yang masuk ke direktori bergantung pada file .d di direktori itu:

 obj/%.o: %.c obj/.d
    $(CC) $(CFLAGS) -c -o $@ $<

Catat penggunaan $ <bukan $ ^.

Terakhir, cegah file .d dihapus secara otomatis:

 .PRECIOUS: %/.d

Melewati file .d, dan bergantung langsung pada direktori, tidak akan berfungsi, karena waktu modifikasi direktori diperbarui setiap kali file ditulis dalam direktori itu, yang akan memaksa pembangunan kembali di setiap pemanggilan make.

Lars Hamrén
sumber
1
Ah, terima kasih, saya mencoba membuat sesuatu seperti ini bekerja. Saya tidak ingin "test -d foo || mkdir foo" pada beberapa tujuan, karena menggunakan "make -j2" akan mengujinya pada waktu yang sama, kedua pengujian memberikan false, lalu dua proses akan mencoba mkdir, terakhir dari mereka gagal.
unhammer
13

Jika memiliki direktori yang sudah ada bukanlah masalah bagi Anda, Anda bisa mengarahkan stderr untuk perintah itu, menyingkirkan pesan kesalahan:

-mkdir $(OBJDIR) 2>/dev/null
andrewdotnich
sumber
Ini memiliki keuntungan bekerja pada Windows juga. Jangan lupa '-' di depan perintah jadi make tidak menjamin.
Michael Burr
11

Di dalam makefile Anda:

target:
    if test -d dir; then echo "hello world!"; else mkdir dir; fi
Lee H.
sumber
10

Di Windows

if not exist "$(OBJDIR)" mkdir $(OBJDIR)

Di Unix | Linux

if [ ! -d "$(OBJDIR)" ]; then mkdir $(OBJDIR); fi
wmad
sumber
Yang ini untuk Windows.
checksum
/bin/sh: 1: Syntax error: end of file unexpected (expecting "then")
wotanii
Versi pertama untuk Windows, opsi kedua berfungsi di Linux seperti yang dikatakan @checksum
Edgard Leal
Yang pertama, Windows, versi tidak atomic sehingga tidak bekerja ketika proses lain (misalnya aturan dalam mode paralel) membuat direktori antara "tidak ada" dan "mkdir".
Petr Vepřek
7
ifeq "$(wildcard $(MY_DIRNAME) )" ""
  -mkdir $(MY_DIRNAME)
endif
Michael McCarty
sumber
Ini bekerja dengan baik untuk mingw's make tanpa membutuhkan alat tambahan.
davenpcj
6
$(OBJDIR):
    mkdir $@

Yang juga berfungsi untuk beberapa direktori, mis.

OBJDIRS := $(sort $(dir $(OBJECTS)))

$(OBJDIRS):
    mkdir $@

Menambahkan $(OBJDIR)sebagai target pertama bekerja dengan baik.

Martin Fido
sumber
3

Ia bekerja di bawah mingw32 / msys / cygwin / linux

ifeq "$(wildcard .dep)" ""
-include $(shell mkdir .dep) $(wildcard .dep/*)
endif
lygstate.dll
sumber
0

Jika Anda secara eksplisit mengabaikan kode pengembalian dan membuang aliran kesalahan maka make Anda akan mengabaikan kesalahan jika itu terjadi:

mkdir 2>/dev/null || true

Ini seharusnya tidak menyebabkan bahaya balapan secara paralel - tetapi saya belum mengujinya untuk memastikannya.

Andrew
sumber
0

Sedikit lebih sederhana dari jawaban Lars:

something_needs_directory_xxx : xxx/..

dan aturan umum:

%/.. : ;@mkdir -p $(@D)

Tidak ada file sentuh untuk dibersihkan atau dibuat. BERHARGA :-)

Jika Anda ingin melihat sedikit trik gmake umum lainnya, atau jika Anda tertarik dengan pembuatan non-rekursif dengan perancah minimal, Anda mungkin perlu memeriksa Dua trik gmake murah lainnya dan pos terkait make lainnya di blog itu.

Mischa
sumber