OS mendeteksi makefile


249

Saya secara rutin bekerja pada beberapa komputer yang berbeda dan beberapa sistem operasi yang berbeda, yaitu Mac OS X, Linux, atau Solaris. Untuk proyek yang sedang saya kerjakan, saya menarik kode saya dari repositori git jarak jauh.

Saya ingin dapat mengerjakan proyek saya terlepas dari terminal mana saya berada. Sejauh ini, saya telah menemukan cara untuk mengatasi perubahan OS dengan mengubah makefile setiap kali saya mengganti komputer. Namun, ini membosankan dan menyebabkan banyak sakit kepala.

Bagaimana saya bisa memodifikasi makefile saya sehingga mendeteksi OS yang saya gunakan dan memodifikasi sintaksis yang sesuai?

Inilah makefile:

cc = gcc -g
CC = g++ -g
yacc=$(YACC)
lex=$(FLEX)

all: assembler

assembler: y.tab.o lex.yy.o
        $(CC) -o assembler y.tab.o lex.yy.o -ll -l y

assembler.o: assembler.c
        $(cc) -o assembler.o assembler.c

y.tab.o: assem.y
        $(yacc) -d assem.y
        $(CC) -c y.tab.c

lex.yy.o: assem.l
        $(lex) assem.l
        $(cc) -c lex.yy.c

clean:
        rm -f lex.yy.c y.tab.c y.tab.h assembler *.o *.tmp *.debug *.acts

Jawaban:


282

Sudah ada banyak jawaban bagus di sini, tetapi saya ingin membagikan contoh yang lebih lengkap bahwa keduanya:

  • tidak menganggap unameada pada Windows
  • juga mendeteksi prosesor

CCFLAGS yang didefinisikan di sini tidak selalu direkomendasikan atau ideal; mereka hanya proyek yang saya tambahkan dengan deteksi otomatis OS / CPU.

ifeq ($(OS),Windows_NT)
    CCFLAGS += -D WIN32
    ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)
        CCFLAGS += -D AMD64
    else
        ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
            CCFLAGS += -D AMD64
        endif
        ifeq ($(PROCESSOR_ARCHITECTURE),x86)
            CCFLAGS += -D IA32
        endif
    endif
else
    UNAME_S := $(shell uname -s)
    ifeq ($(UNAME_S),Linux)
        CCFLAGS += -D LINUX
    endif
    ifeq ($(UNAME_S),Darwin)
        CCFLAGS += -D OSX
    endif
    UNAME_P := $(shell uname -p)
    ifeq ($(UNAME_P),x86_64)
        CCFLAGS += -D AMD64
    endif
    ifneq ($(filter %86,$(UNAME_P)),)
        CCFLAGS += -D IA32
    endif
    ifneq ($(filter arm%,$(UNAME_P)),)
        CCFLAGS += -D ARM
    endif
endif

8
Sayangnya PROCESSOR_ARCHITECTUREenvvar tampaknya tervirtualisasi tergantung pada apakah prosesnya 32-bit atau 64-bit. Jadi, jika Anda make32-bit dan Anda mencoba membangun aplikasi 64-bit, itu akan gagal. Menggunakannya dalam kombinasi dengan PROCESSOR_ARCHITEW6432bekerja untuk saya (lihat ini , dan itu )
Thomas

4
Alangkah baiknya jika maketim menambahkan beberapa variabel ajaib dengan os dan lengkungan, mungkin terlalu banyak kesulitan.
Alex

6
@ JanusTroelsen: tidak masalah apakah OSdiatur pada sistem non-Windows. Buat suguhan tidak disetel sama dengan kosong, yang akan menyebabkan lompatan ke unameblok berbasis. Anda hanya perlu menambahkan cek FreeBSD di sana.
Trevor Robinson

3
ini rusak pada osx juga. /bin/sh: -c: line 0: syntax error near unexpected token , Windows_NT '/ bin / sh: -c: line 0:ifeq (,Windows_NT)' make: *** [os] Error 2
k107

1
@ Kristi Sepertinya Anda menjalankan ini sebagai perintah shell dan tidak dalam konteks arahan makefile.
phord

119

Perintah uname ( http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/uname.1.html ) tanpa parameter akan memberi tahu Anda nama sistem operasi. Saya akan menggunakannya, kemudian membuat persyaratan berdasarkan nilai pengembalian.

Contoh

UNAME := $(shell uname)

ifeq ($(UNAME), Linux)
# do something Linux-y
endif
ifeq ($(UNAME), Solaris)
# do something Solaris-y
endif

Untuk lebih jelasnya, baris itu ada di Makefile Anda. Saya baru saja mencoba konstruk itu di Makefiles di Cygwin dan OSX, dan itu berfungsi seperti yang diharapkan. Sesuatu untuk dicoba: Ketikkan ketenaran pada baris perintah Anda. Itu akan memberi tahu Anda nilai untuk OS itu. OSX kemungkinan akan menjadi "Darwin".
dbrown0708

Proyek GnuWin32 memiliki kedua nama dan Gnu yang tersedia sebagai aplikasi Windows asli, membuat teknik ini mudah dibawa ke MingW pada prompt perintah dan juga Cygwin di Windows.
RBerteig

Gagal pada mesin Solaris saya ketika di dalam makefile. Perintah uname tersedia di sistem itu.
samoz

4
Perhatikan bahwa jika Anda meletakkan ini di dalam Target Maket, target tersebut tidak boleh diindentasi.
nylund

4
Bukankah sintaks ": =" khusus untuk GNU Make?
Ankur Sethi

40

Deteksi sistem operasi menggunakan dua trik sederhana:

  • Pertama variabel lingkungan OS
  • Lalu unameperintahnya
ifeq ($(OS),Windows_NT)     # is Windows_NT on XP, 2000, 7, Vista, 10...
    detected_OS := Windows
else
    detected_OS := $(shell uname)  # same as "uname -s"
endif

Atau cara yang lebih aman, jika tidak pada Windows dan unametidak tersedia:

ifeq ($(OS),Windows_NT) 
    detected_OS := Windows
else
    detected_OS := $(shell sh -c 'uname 2>/dev/null || echo Unknown')
endif

Ken Jackson mengusulkan alternatif yang menarik jika Anda ingin membedakan Cygwin / MinGW / MSYS / Windows. Lihat jawabannya yang terlihat seperti itu:

ifeq '$(findstring ;,$(PATH))' ';'
    detected_OS := Windows
else
    detected_OS := $(shell uname 2>/dev/null || echo Unknown)
    detected_OS := $(patsubst CYGWIN%,Cygwin,$(detected_OS))
    detected_OS := $(patsubst MSYS%,MSYS,$(detected_OS))
    detected_OS := $(patsubst MINGW%,MSYS,$(detected_OS))
endif

Kemudian Anda dapat memilih hal-hal yang relevan tergantung pada detected_OS:

ifeq ($(detected_OS),Windows)
    CFLAGS += -D WIN32
endif
ifeq ($(detected_OS),Darwin)        # Mac OS X
    CFLAGS += -D OSX
endif
ifeq ($(detected_OS),Linux)
    CFLAGS   +=   -D LINUX
endif
ifeq ($(detected_OS),GNU)           # Debian GNU Hurd
    CFLAGS   +=   -D GNU_HURD
endif
ifeq ($(detected_OS),GNU/kFreeBSD)  # Debian kFreeBSD
    CFLAGS   +=   -D GNU_kFreeBSD
endif
ifeq ($(detected_OS),FreeBSD)
    CFLAGS   +=   -D FreeBSD
endif
ifeq ($(detected_OS),NetBSD)
    CFLAGS   +=   -D NetBSD
endif
ifeq ($(detected_OS),DragonFly)
    CFLAGS   +=   -D DragonFly
endif
ifeq ($(detected_OS),Haiku)
    CFLAGS   +=   -D Haiku
endif

Catatan:

  • Perintah unamesama dengan uname -skarena opsi -s( --kernel-name) adalah default. Lihat mengapa uname -slebih baik daripadauname -o .

  • Penggunaan OS(bukan uname) menyederhanakan algoritma identifikasi. Anda masih dapat menggunakan semata-mata uname, tetapi Anda harus berurusan dengan if/elseblok untuk memeriksa semua variasi MinGW, Cygwin, dll.

  • Variabel lingkungan OSselalu disetel "Windows_NT"pada versi Windows yang berbeda (lihat %OS%variabel lingkungan di Wikipedia ).

  • Alternatif OSadalah variabel lingkungan MSVC(memeriksa keberadaan MS Visual Studio , lihat contoh menggunakan Visual C ++ ).


Di bawah ini saya memberikan contoh lengkap menggunakan makedan gccmembangun perpustakaan bersama: *.soatau *.dlltergantung pada platform. Contohnya sesederhana mungkin untuk lebih dimengerti.

Untuk menginstal makedan gccpada Windows, lihat Cygwin atau MinGW .

Contoh saya didasarkan pada lima file

 ├── lib
    └── Makefile
    └── hello.h
    └── hello.c
 └── app
     └── Makefile
     └── main.c

Pengingat: Makefile indentasi menggunakan tabulasi . Perhatian saat menyalin-menempel di bawah file sampel.

Kedua Makefilefile tersebut

1. lib/Makefile

ifeq ($(OS),Windows_NT)
    uname_S := Windows
else
    uname_S := $(shell uname -s)
endif

ifeq ($(uname_S), Windows)
    target = hello.dll
endif
ifeq ($(uname_S), Linux)
    target = libhello.so
endif
#ifeq ($(uname_S), .....) #See https://stackoverflow.com/a/27776822/938111
#    target = .....
#endif

%.o: %.c
    gcc  -c $<  -fPIC  -o $@
    # -c $<  => $< is first file after ':' => Compile hello.c
    # -fPIC  => Position-Independent Code (required for shared lib)
    # -o $@  => $@ is the target => Output file (-o) is hello.o

$(target): hello.o
    gcc  $^  -shared  -o $@
    # $^      => $^ expand to all prerequisites (after ':') => hello.o
    # -shared => Generate shared library
    # -o $@   => Output file (-o) is $@ (libhello.so or hello.dll)

2. app/Makefile

ifeq ($(OS),Windows_NT)
    uname_S := Windows
else
    uname_S := $(shell uname -s)
endif

ifeq ($(uname_S), Windows)
    target = app.exe
endif
ifeq ($(uname_S), Linux)
    target = app
endif
#ifeq ($(uname_S), .....) #See https://stackoverflow.com/a/27776822/938111
#    target = .....
#endif

%.o: %.c
    gcc  -c $< -I ../lib  -o $@
    # -c $<     => compile (-c) $< (first file after :) = main.c
    # -I ../lib => search headers (*.h) in directory ../lib
    # -o $@     => output file (-o) is $@ (target) = main.o

$(target): main.o
    gcc  $^  -L../lib  -lhello  -o $@
    # $^       => $^ (all files after the :) = main.o (here only one file)
    # -L../lib => look for libraries in directory ../lib
    # -lhello  => use shared library hello (libhello.so or hello.dll)
    # -o $@    => output file (-o) is $@ (target) = "app.exe" or "app"

Untuk mempelajari lebih lanjut, baca dokumentasi Variabel Otomatis seperti yang ditunjukkan oleh cfi .

Kode sumber

- lib/hello.h

#ifndef HELLO_H_
#define HELLO_H_

const char* hello();

#endif

- lib/hello.c

#include "hello.h"

const char* hello()
{
    return "hello";
}

- app/main.c

#include "hello.h" //hello()
#include <stdio.h> //puts()

int main()
{
    const char* str = hello();
    puts(str);
}

Membangun

Perbaiki salin-tempel Makefile(ganti spasi terdepan dengan satu tabulasi).

> sed  's/^  */\t/'  -i  */Makefile

The makeperintah yang sama pada kedua platform. Output yang diberikan adalah pada OS mirip Unix:

> make -C lib
make: Entering directory '/tmp/lib'
gcc  -c hello.c  -fPIC  -o hello.o
# -c hello.c  => hello.c is first file after ':' => Compile hello.c
# -fPIC       => Position-Independent Code (required for shared lib)
# -o hello.o  => hello.o is the target => Output file (-o) is hello.o
gcc  hello.o  -shared  -o libhello.so
# hello.o        => hello.o is the first after ':' => Link hello.o
# -shared        => Generate shared library
# -o libhello.so => Output file (-o) is libhello.so (libhello.so or hello.dll)
make: Leaving directory '/tmp/lib'

> make -C app
make: Entering directory '/tmp/app'
gcc  -c main.c -I ../lib  -o main.o
# -c main.c => compile (-c) main.c (first file after :) = main.cpp
# -I ../lib => search headers (*.h) in directory ../lib
# -o main.o => output file (-o) is main.o (target) = main.o
gcc  main.o  -L../lib  -lhello  -o app
# main.o   => main.o (all files after the :) = main.o (here only one file)
# -L../lib => look for libraries in directory ../lib
# -lhello  => use shared library hello (libhello.so or hello.dll)
# -o app   => output file (-o) is app.exe (target) = "app.exe" or "app"
make: Leaving directory '/tmp/app'

Lari

Aplikasi ini perlu tahu di mana perpustakaan bersama.

Di Windows, solusi sederhana adalah menyalin pustaka tempat aplikasi tersebut:

> cp -v lib/hello.dll app
`lib/hello.dll' -> `app/hello.dll'

Pada OS yang mirip Unix, Anda dapat menggunakan LD_LIBRARY_PATHvariabel lingkungan:

> export LD_LIBRARY_PATH=lib

Jalankan perintah pada Windows:

> app/app.exe
hello

Jalankan perintah pada OS yang mirip Unix:

> app/app
hello

Saya menghargai upaya Anda, tetapi pertanyaan utamanya adalah mendeteksi sistem operasi. Contoh Anda hanya mendeteksi Linux dan sebaliknya langsung menganggap Windows.
Shahbaz

Hai, Shahbaz. Anda benar, jawaban saya tidak memberikan pendekatan yang berbeda dari jawaban lainnya. Apalagi skrip saya menganggap platform adalah Windows padahal unamebukan Linux. Saya memberikan contoh yang mungkin tidak Anda butuhkan, tetapi ini dapat membantu seseorang mencari (di web) cara untuk menerapkan Makefileuntuk kedua platform ;-) Apa yang harus saya ubah dalam jawaban saya? Cheers
olibre

cobalah untuk menemukan cara untuk mengidentifikasi dengan benar sistem operasi lain juga! Tujuannya adalah untuk menemukan metode yang tidak terlalu rumit, tetapi yang lebih penting adalah bukti peluru. Yaitu, itu tidak akan membuat kesalahan apapun yang terjadi.
Shahbaz

1
@ Colibre Terima kasih atas contoh terperinci, sangat dihargai dan membantu saya memulai dengan cepat. Dalam lib/Makefilecontoh, targetdigunakan untuk .sovs .dll. Contoh paralel untuk app/makefileakan berguna untuk perbandingan empty stringvs .exeuntuk nama file aplikasi. Sebagai contoh, saya biasanya tidak melihat app.exeOS seperti Unix. ;-)

1
LSF? LFS? Salah ketik?
Franklin Yu

19

Saya baru-baru ini bereksperimen untuk menjawab pertanyaan ini, saya bertanya pada diri sendiri. Inilah kesimpulan saya:

Karena di Windows, Anda tidak dapat memastikan bahwa unameperintah itu tersedia, Anda dapat menggunakannya gcc -dumpmachine. Ini akan menampilkan target kompiler.

Mungkin juga ada masalah ketika menggunakan unamejika Anda ingin melakukan kompilasi silang.

Berikut adalah contoh daftar kemungkinan keluaran gcc -dumpmachine:

  • mingw32
  • i686-pc-cygwin
  • x86_64-redhat-linux

Anda dapat memeriksa hasilnya di makefile seperti ini:

SYS := $(shell gcc -dumpmachine)
ifneq (, $(findstring linux, $(SYS)))
 # Do Linux things
else ifneq(, $(findstring mingw, $(SYS)))
 # Do MinGW things
else ifneq(, $(findstring cygwin, $(SYS)))
 # Do Cygwin things
else
 # Do things for others
endif

Ini bekerja dengan baik untuk saya, tetapi saya tidak yakin itu cara yang dapat diandalkan untuk mendapatkan tipe sistem. Setidaknya itu dapat diandalkan tentang MinGW dan itu saja yang saya butuhkan karena tidak perlu memiliki unameperintah atau paket MSYS di Windows.

Singkatnya, unamememberikan Anda sistem di mana Anda kompilasi, dan gcc -dumpmachinememberikan Anda sistem untuk yang Anda kompilasi.


Ini poin yang bagus. Namun, tidakkah unameikut MinGW? Namun demikian, catatan tambahan tentang kompilasi silang sangat bagus.
Shahbaz

2
Pengaturan @Shahbaz MinGW dapat menginstal MSYS (yang berisi tidak nama) tetapi itu opsional. Masih mungkin untuk menemukan sistem dengan hanya alat gcc MinGW
phsym

1
Itu tidak berfungsi di mana pun Dentang adalah kompiler default, seperti OS X dan FreeBSD.
MarcusJ

@SebastianGodelet @MarcusJ Perbaikan yang mudah adalah $(shell $(CC) -dumpmachine). Pada OS X Sierra, perintah -dumpmachine bekerja pada Dentang.
Vortico

Pada OS X 10.12.5 itu x86_64-apple-darwin16.6.0dan bahwa karya cuaca Anda menyebutnya gcc, ccatau clang, tetapi tidakcl
MarcusJ

17

The git makefile berisi banyak contoh bagaimana mengelola tanpa autoconf / automake, namun masih bekerja pada banyak platform unixy.


13
Mengetahui bahwa Git tidak menggunakan Autofools entah bagaimana membuat saya merasa dibenarkan dalam keengganan saya kepada mereka ...
Dan Mould

11
"Autofools"? Apakah itu kesalahan ketik yang disengaja? :)
JesperE

6
Dulu. Tapi setelah dipikir-pikir, saya pikir saya lebih suka "Autostools". : D
Dan Moulding

Btw, siapa yang kamu maksud dengan "mereka"? Orang-orang Git atau Autotools? : D
JesperE

8
Bahasa Inggris adalah bahasa yang tidak tepat. Bagaimana dengan ini: if (!usesAutotools(git)) aversionTo(autotools) = justified;Saya akan mengklarifikasi juga bahwa hanya alat yang saya benci. Saya yakin orang Autotools adalah orang baik.
Dan Moulding

11

Pembaruan: Saya sekarang menganggap jawaban ini sudah usang. Saya memposting solusi sempurna baru lebih jauh ke bawah.

Jika makefile Anda dapat berjalan di Windows non-Cygwin, unamemungkin tidak tersedia. Itu aneh, tapi ini solusi potensial. Anda harus memeriksa Cygwin terlebih dahulu untuk mengesampingkannya, karena memiliki WINDOWS dalam PATHvariabel lingkungannya juga.

ifneq (,$(findstring /cygdrive/,$(PATH)))
    UNAME := Cygwin
else
ifneq (,$(findstring WINDOWS,$(PATH)))
    UNAME := Windows
else
    UNAME := $(shell uname -s)
endif
endif

Ini bagus untuk sekarang! Bisakah Anda memberi tahu saya satu hal? Saya tidak menggunakan Cygwin, tapi saya sudah menginstal MinGW dengan path bin-nya di PATH. Jika saya mengeluarkan unamedari terminal cmd normal itu memberi saya MINGW. Maksud saya adalah, saya masih punya unametanpa menggunakan Cygwin. Saya juga punya git bash, tapi saya belum mencoba yang belum terkenal (sekarang saya di Linux). Bisakah Anda memberi tahu saya bagaimana keduanya dapat dimasukkan ke dalam kode Anda?
Shahbaz

Jika Anda yakin uname tersedia, itu solusi terbaik. Tetapi di lingkungan saya, semua orang menggunakan Windows dan hanya sedikit orang yang menginstal cygwin atau mingw , jadi saya tidak punya jaminan bahwa apa pun bahkan standar seperti uname akan berfungsi. Saat ini saya mengalami kesulitan dengan kode di atas menjalankan make.exe di shell cmd. Windows adalah platform yang sangat menyebalkan untuk digunakan.
Ken Jackson

Maksud saya adalah, sebelum menguji keberadaan WINDOWS di PATH, Anda pastikan Anda tidak berurusan dengan cygwin, bagaimana Anda bisa memastikan Anda tidak berurusan dengan MinGW? Sebagai contoh, apakah mungkin di Makefile untuk menguji apakah suatu perintah dapat dijalankan, dan jika unametidak dapat dijalankan kita akan mengerti kita berada di Windows?
Shahbaz

Saya berjuang untuk menemukan solusi bersih untuk Mingw / cygwin / shell-or-cmd / Linux ini juga. Pada akhirnya, sesuatu seperti premake atau cmake sepertinya ide terbaik.
Isaac Nequittepas

Ini bukan lagi solusi terbaik. Solusi baru yang saya posting membedakan Windows asli dengan mencari ';' dalam variabel PATH, tanpa panggilan shell.
Ken Jackson

7

Itulah pekerjaan yang dirancang untuk diselesaikan oleh automake / autoconf GNU . Anda mungkin ingin menyelidikinya.

Atau Anda dapat mengatur variabel lingkungan pada platform yang berbeda dan membuat Makefile bersyarat terhadapnya.


11
Saya sangat menyarankan agar tidak menggunakan automake / autoconf. Mereka membosankan untuk digunakan, menambahkan banyak overhead ke file Anda, ke waktu pembuatan Anda. Mereka hanya menambah kompleksitas untuk efek yang biasanya sangat kecil (masih tidak mudah dibawa antar sistem).
Johannes Overmann

1
Saya hanya menghabiskan beberapa hari belajar makeuntuk melakukan apa yang saya inginkan. Apakah saya sekarang ingin masuk ke automake / autoconf juga? - TIDAK. Apa yang bisa dilakukan di makefile, tentu harus dilakukan di makefile, kalau saja supaya saya tidak memiliki beberapa poin perhentian setiap kali saya ingin mengubah kompilasi & tautan.
Insinyur

Berapa banyak platform yang didukung makefile Anda? automake dan autoconf benar-benar menjadi milik mereka ketika Anda menginginkan portabilitas ke banyak platform.
Douglas Leeder 6-15

2
Saya tidak akan memerlukan dependensi yang tidak berguna, dan mengubah seluruh sistem build saya hanya untuk mencari tahu OS apa yang sedang dikompilasi.
MarcusJ

7

Saya akhirnya menemukan solusi sempurna yang menyelesaikan masalah ini untuk saya.

ifeq '$(findstring ;,$(PATH))' ';'
    UNAME := Windows
else
    UNAME := $(shell uname 2>/dev/null || echo Unknown)
    UNAME := $(patsubst CYGWIN%,Cygwin,$(UNAME))
    UNAME := $(patsubst MSYS%,MSYS,$(UNAME))
    UNAME := $(patsubst MINGW%,MSYS,$(UNAME))
endif

Variabel UNAME diatur ke Linux, Cygwin, MSYS, Windows, FreeBSD, NetBSD (atau mungkin Solaris, Darwin, OpenBSD, AIX, HP-UX), atau Tidak Diketahui. Itu kemudian dapat dibandingkan sepanjang sisa Makefile untuk memisahkan variabel dan perintah OS-sensitif.

Kuncinya adalah bahwa Windows menggunakan titik koma untuk memisahkan jalur dalam variabel PATH sedangkan orang lain menggunakan titik dua. (Dimungkinkan untuk membuat direktori Linux dengan tanda ';' di namanya dan menambahkannya ke PATH, yang akan memecahkan ini, tetapi siapa yang akan melakukan hal seperti itu?) Ini tampaknya menjadi metode yang paling tidak berisiko untuk mendeteksi Windows asli karena itu tidak perlu panggilan shell. The Cygwin dan MSYS PATH menggunakan titik dua begitu uname disebut bagi mereka.

Perhatikan bahwa variabel lingkungan OS dapat digunakan untuk mendeteksi Windows, tetapi tidak untuk membedakan antara Cygwin dan Windows asli. Pengujian untuk gema kutipan berhasil, tetapi membutuhkan panggilan shell.

Sayangnya, Cygwin menambahkan beberapa informasi versi ke output uname , jadi saya menambahkan panggilan 'patsubst' untuk mengubahnya hanya 'Cygwin'. Juga, uname untuk MSYS sebenarnya memiliki tiga kemungkinan keluaran dimulai dengan MSYS atau MINGW, tetapi saya juga menggunakan patsubst untuk mengubah semua menjadi hanya 'MSYS'.

Jika penting untuk membedakan antara sistem Windows asli dengan dan tanpa beberapa uname.exe di jalur, baris ini dapat digunakan sebagai pengganti penugasan sederhana:

UNAME := $(shell uname 2>NUL || echo Windows)

Tentu saja dalam semua kasus GNU make diperlukan, atau make lain yang mendukung fungsi yang digunakan.


6

Saya mengalami masalah ini hari ini dan saya membutuhkannya di Solaris jadi di sini ada cara standar POSIX untuk melakukan (sesuatu yang sangat dekat dengan) ini.

#Detect OS
UNAME = `uname`

# Build based on OS name
DetectOS:
    -@make $(UNAME)


# OS is Linux, use GCC
Linux: program.c
    @SHELL_VARIABLE="-D_LINUX_STUFF_HERE_"
    rm -f program
    gcc $(SHELL_VARIABLE) -o program program.c

# OS is Solaris, use c99
SunOS: program.c
    @SHELL_VARIABLE="-D_SOLARIS_STUFF_HERE_"
    rm -f program
    c99 $(SHELL_VARIABLE) -o program program.c

1
Mendapatkan kesalahan pada OSX: "Makefile: 22: *** pemisah yang hilang. Berhenti." Pada baris ini: "- @ make $ (UNAME_S)".
Czarek Tomczak

OSX mungkin tidak sesuai jadi coba ini secara berurutan. (1) Pastikan Anda menggunakan TAB sebagai karakter pertama pada baris (2) Hapus "- @" di depan make (2a) Jika 2 bekerja, coba satu karakter dan kemudian yang lain (3) Pastikan bahwa UNAME_S didefinisikan, coba gema $ (UNAME_S) daripada - @ make $ (UNAME_S)
Huckle

6

Berikut adalah solusi sederhana yang memeriksa apakah Anda berada di lingkungan Windows atau posix-like (Linux / Unix / Cygwin / Mac):

ifeq ($(shell echo "check_quotes"),"check_quotes")
   WINDOWS := yes
else
   WINDOWS := no
endif

Ini mengambil keuntungan dari kenyataan bahwa gema ada di kedua lingkungan seperti posix dan Windows, dan di Windows shell tidak menyaring tanda kutip.


1
Cukup tidak aman karena $PATHmungkin merujuk ke yang lain echo(
Milik

@YoYoYonnY Mengapa jalur Anda merujuk ke gema lain? Sepertinya situasi yang sangat tidak mungkin.
Samuel

1
tidak juga, git yang melakukannya, mingw yang melakukannya, cygwin yang melakukannya ... Dan saya pribadi menempatkan C: \ Windows \ System32 di bagian bawah jalur saya.
yyny

1
"Solusi" "ini berfungsi" untuk semua lingkungan, tetapi poin saya adalah bahwa ini jelas tidak mendeteksi windows dengan aman. Jika saya ingin menetapkan -mwindowsbendera atau memilih antara .dllatau .so, ini akan gagal.
yyny

1
@YoYoYonnY Terima kasih telah menjelaskan. Dalam situasi saya, saya hanya peduli jika saya berada di lingkungan Cygwin atau Windows atau Linux daripada di OS mana saya, jadi ini sangat membantu bagi saya. Kedengarannya seperti kebutuhan Anda berbeda dari apa yang saya miliki.
Samuel

3

Perhatikan bahwa Makefiles sangat sensitif terhadap spasi. Berikut adalah contoh Makefile yang menjalankan perintah tambahan pada OS X dan yang berfungsi pada OS X dan Linux. Namun secara keseluruhan, autoconf / automake adalah cara untuk melakukan apa saja yang tidak sepele.

UNAME: = $ (shell uname -s)
CPP = g ++
CPPFLAGS = -pthread -ansi -Wall -Werror -pedantic -O0 -g3 -I / nexopia / termasuk
LDFLAGS = -pthread -L / nexopia / lib -lboost_system

HEADERS = data_structures.h http_client.h load.h lock.h search.h server.h thread.h utility.h
OBJECTS = http_client.o load.o lock.o search.o server.o utas.o utilitas.o vor.o

semua: vor

bersih:
    rm -f $ (OBJECTS) vor

untuk: $ (BENDA)
    $ (CPP) $ (LDFLAGS) -o vor $ (OBJECTS)
ifeq ($ (UNAME), Darwin)
    # Tetapkan lokasi perpustakaan Boost
    install_name_tool -change libboost_system.dylib /nexopia/lib/libboost_system.dylib vor
berakhir jika

% .o:% .cpp $ (HEADERS) Makefile
    $ (CPP) $ (CPPFLAGS) -c $

2

Cara lain untuk melakukan ini adalah dengan menggunakan skrip "configure". Jika Anda sudah menggunakan satu dengan makefile Anda, Anda bisa menggunakan kombinasi uname dan sed untuk menyelesaikannya. Pertama, dalam skrip Anda, lakukan:

UNAME=uname

Kemudian, untuk memasukkan ini ke dalam Makefile Anda, mulailah dengan Makefile.in yang seharusnya memiliki sesuatu seperti

UNAME=@@UNAME@@

di dalamnya.

Gunakan perintah sed berikut di skrip configure Anda setelah UNAME=unamebit.

sed -e "s|@@UNAME@@|$UNAME|" < Makefile.in > Makefile

Sekarang makefile Anda harus UNAMEdidefinisikan sesuai keinginan. Jika hanya pernyataan / elif / else yang tersisa!


Bukankah seharusnya yang pertama menyamakan ini? UNAME = $ (tidak sama)
Ken Jackson

0

Saya punya kasus di mana saya harus mendeteksi perbedaan antara dua versi Fedora, untuk mengubah opsi baris perintah untuk inkscape:
- di Fedora 31, inkscape default adalah 1.0beta yang menggunakan--export-file
- di Fedora <31, inkscape default adalah 0,92 yang digunakan--export-pdf

Makefile saya berisi yang berikut ini

# set VERSION_ID from /etc/os-release

$(eval $(shell grep VERSION_ID /etc/os-release))

# select the inkscape export syntax

ifeq ($(VERSION_ID),31)
EXPORT = export-file
else
EXPORT = export-pdf
endif

# rule to convert inkscape SVG (drawing) to PDF

%.pdf : %.svg
    inkscape --export-area-drawing $< --$(EXPORT)=$@

Ini berfungsi karena /etc/os-releasemengandung garis

VERSION_ID=<value>

jadi perintah shell di Makefile mengembalikan string VERSION_ID=<value>, maka perintah eval bertindak atas ini untuk mengatur variabel Makefile VERSION_ID. Ini jelas dapat diubah untuk OS lain tergantung bagaimana metadata disimpan. Perhatikan bahwa di Fedora tidak ada variabel lingkungan default yang memberikan versi OS, kalau tidak saya akan menggunakannya!

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.