ระบบตรวจจับ makefile


250

ฉันทำงานเป็นประจำกับคอมพิวเตอร์หลายเครื่องและระบบปฏิบัติการหลายระบบซึ่ง ได้แก่ Mac OS X, Linux หรือ Solaris สำหรับโครงการที่ฉันกำลังทำงานฉันจะดึงรหัสจากที่เก็บ git ระยะไกล

ฉันชอบที่จะสามารถทำงานในโครงการของฉันได้ จนถึงตอนนี้ฉันได้พบวิธีที่จะหลีกเลี่ยงการเปลี่ยนแปลงระบบปฏิบัติการด้วยการเปลี่ยน makefile ทุกครั้งที่ฉันสลับคอมพิวเตอร์ อย่างไรก็ตามสิ่งนี้น่าเบื่อและทำให้ปวดศีรษะเป็นพวง

ฉันจะแก้ไข makefile ของฉันได้อย่างไรเพื่อตรวจสอบว่าฉันใช้ระบบปฏิบัติการใดอยู่และแก้ไขไวยากรณ์ตามนั้น

นี่คือ 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

คำตอบ:


283

มีคำตอบที่ดีมากมายที่นี่แล้ว แต่ฉันต้องการแบ่งปันตัวอย่างที่สมบูรณ์ยิ่งขึ้นที่ทั้งสอง:

  • ไม่ถือว่าunameมีอยู่ใน Windows
  • ตรวจจับโปรเซสเซอร์

CCFLAGS ที่กำหนดไว้ที่นี่ไม่จำเป็นต้องแนะนำหรือเหมาะ เป็นเพียงโครงการที่ฉันเพิ่มระบบตรวจจับอัตโนมัติ 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
น่าเสียดายที่PROCESSOR_ARCHITECTUREenvvar ดูเหมือนจะเป็นเสมือนจริงขึ้นอยู่กับว่ากระบวนการนั้นเป็นแบบ 32 บิตหรือ 64 บิต ดังนั้นถ้าคุณmakeเป็น 32- บิตและคุณกำลังพยายามสร้างแอปพลิเคชัน 64 บิตมันจะล้มเหลว ใช้มันร่วมกับPROCESSOR_ARCHITEW6432ทำงานให้ฉัน (ดูนี้และที่ )
โทมัส

4
น่าจะดีถ้าmakeทีมเพิ่มตัวแปรเวทคู่กับระบบปฏิบัติการและอาร์คอาจเป็นปัญหามากเกินไป
อเล็กซ์

6
@JanusTroelsen: มันไม่สำคัญว่าจะOSมีการตั้งค่าในระบบที่ไม่ใช่ Windows ทำให้ถือว่าไม่มีการตั้งค่าเช่นเดียวกับที่ว่างเปล่าซึ่งจะทำให้เกิดการกระโดดไปยังunameบล็อก -based คุณเพียงแค่ต้องเพิ่ม FreeBSD ตรวจสอบที่นั่น
Trevor Robinson

3
ตัวแบ่งนี้บน osx ด้วย /bin/sh: -c: line 0: syntax error near unexpected token , Windows_NT '/ bin / sh: -c: บรรทัด 0:ifeq (,Windows_NT)' make: *** [os] Error 2
k107

1
@kristi ดูเหมือนว่าคุณจะเรียกใช้งานเป็นคำสั่งเชลล์และไม่อยู่ในบริบทของคำสั่ง makefile
phord

119

คำสั่ง uname ( http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/uname.1.html ) ที่ไม่มีพารามิเตอร์ควรบอกชื่อระบบปฏิบัติการให้คุณ ฉันจะใช้มันแล้วสร้างเงื่อนไขขึ้นอยู่กับค่าส่งคืน

ตัวอย่าง

UNAME := $(shell uname)

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

เพียงเพื่อให้ชัดเจนบรรทัดนั้นไปใน Makefile ของคุณ ฉันเพิ่งลองสร้างมันใน Makefiles ใน Cygwin และ OSX และทำงานได้ตามที่คาดไว้ สิ่งที่ต้องลอง: พิมพ์ uname บนบรรทัดคำสั่งของคุณ นั่นจะบอกคุณค่าสำหรับระบบปฏิบัติการนั้น OSX น่าจะเป็น "ดาร์วิน"
dbrown0708

โครงการ GnuWin32 มีทั้ง uname และ Gnu ที่พร้อมใช้งานเป็นแอปพลิเคชัน Windows ดั้งเดิมทำให้เทคนิคนี้สามารถพกพาไปยัง MingW ได้ที่หน้าจอพร้อมรับคำสั่งและ Cygwin บน Windows
RBerteig

มันล้มเหลวในเครื่อง Solaris ของฉันเมื่ออยู่ใน makefile คำสั่ง uname พร้อมใช้งานบนระบบนั้น
samoz

4
โปรดทราบว่าหากคุณวางสิ่งนี้ไว้ใน Maket เป้าหมายจะต้องไม่เยื้อง
nylund

4
ไม่ใช่ไวยากรณ์ ": =" เฉพาะกับ GNU Make ใช่หรือไม่
Ankur Sethi

40

ตรวจสอบระบบปฏิบัติการโดยใช้สองเทคนิคง่ายๆ:

  • ก่อนอื่นตัวแปรสภาพแวดล้อม OS
  • จากนั้นunameคำสั่ง
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

หรือวิธีที่ปลอดภัยกว่านี้หากไม่ใช่ใน Windows และunameไม่สามารถใช้งานได้:

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

Ken Jacksonเสนอทางเลือกที่น่าสนใจหากคุณต้องการแยก Cygwin / MinGW / MSYS / Windows ดูคำตอบของเขาที่ดูเหมือนว่า:

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

จากนั้นคุณสามารถเลือกสิ่งที่เกี่ยวข้องขึ้นอยู่กับ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

หมายเหตุ:

  • คำสั่งunameเหมือนกับuname -sเพราะ option -s( --kernel-name) เป็นค่าเริ่มต้น ดูเหตุผลที่uname -suname -oดีกว่า

  • การใช้OS(แทนuname) ลดความซับซ้อนของอัลกอริทึมการระบุ คุณยังสามารถใช้ แต่เพียงผู้เดียวunameแต่คุณต้องจัดการกับif/elseบล็อกเพื่อตรวจสอบรูปแบบ MinGW, Cygwin ฯลฯ ทั้งหมด

  • ตัวแปรสภาพแวดล้อมOSถูกตั้งค่าเป็น"Windows_NT"เวอร์ชัน Windows ที่แตกต่างกันเสมอ(ดู%OS%ตัวแปรสภาพแวดล้อมบน Wikipedia )

  • อีกทางเลือกหนึ่งOSคือตัวแปรสภาพแวดล้อมMSVC(ตรวจสอบสถานะของMS Visual Studioดูตัวอย่างการใช้ Visual C ++ )


ด้านล่างนี้เป็นตัวอย่างที่สมบูรณ์สำหรับการใช้makeและgccการสร้างไลบรารี่ที่แชร์: *.soหรือ*.dllขึ้นอยู่กับแพลตฟอร์ม ตัวอย่างนี้ง่ายที่สุดที่จะเข้าใจได้ง่ายขึ้น

การติดตั้งmakeและgccบน Windows ดูCygwinหรือMinGW

ตัวอย่างของฉันใช้ไฟล์ห้าไฟล์

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

คำเตือน: Makefileเป็นเยื้องโดยใช้การจัดระเบียบ ข้อควรระวังเมื่อคัดลอกวางด้านล่างไฟล์ตัวอย่าง

ทั้งสองMakefileไฟล์

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"

ต้องการเรียนรู้เพิ่มเติมโปรดอ่านตัวแปรอัตโนมัติเอกสารเป็นแหลมออกโดยCFI

รหัสแหล่งที่มา

- 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);
}

การสร้าง

แก้ไขการคัดลอกวางMakefile(แทนที่ช่องว่างนำหน้าด้วยหนึ่งตาราง)

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

makeคำสั่งเดียวกันในทั้งสองแพลตฟอร์ม เอาต์พุตที่กำหนดอยู่บน Unix-like OSes:

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

วิ่ง

แอปพลิเคชั่นต้องรู้ว่าไลบรารีที่แชร์อยู่ที่ไหน

บน Windows ทางออกที่ง่ายคือการคัดลอกไลบรารี่ที่แอปพลิเคชันอยู่:

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

บนระบบปฏิบัติการ Unix-like OSes คุณสามารถใช้LD_LIBRARY_PATHตัวแปรสภาพแวดล้อม:

> export LD_LIBRARY_PATH=lib

เรียกใช้คำสั่งบน Windows:

> app/app.exe
hello

รันคำสั่งบน OSE ที่เหมือน Unix:

> app/app
hello

ฉันขอขอบคุณความพยายามของคุณ แต่คำถามหลักคือการตรวจสอบระบบปฏิบัติการ ตัวอย่างของคุณตรวจพบลินุกซ์เท่านั้นและถือว่า Windows ใช้งานไม่ได้
Shahbaz

สวัสดี @Shahbaz คุณถูกต้องคำตอบของฉันไม่ได้ให้แนวทางที่แตกต่างจากคำตอบอื่น ๆ ยิ่งกว่านั้นสคริปต์ของฉันถือว่าแพลตฟอร์มเป็น Windows เมื่อunameไม่ใช่ Linux ฉันให้ตัวอย่างที่คุณอาจไม่ต้องการ แต่สิ่งนี้อาจช่วยให้คนค้นหา (บนเว็บ) วิธีที่จะใช้Makefileสำหรับทั้งสองแพลตฟอร์ม ;-) ฉันควรเปลี่ยนอะไรในคำตอบของฉัน ไชโย
โอลิเบอร์

พยายามหาวิธีในการระบุระบบปฏิบัติการอื่น ๆ ให้ถูกต้องเช่นกัน! เป้าหมายคือการหาวิธีการที่ไม่ซับซ้อนเกินไป แต่ที่สำคัญกว่านั้นคือระบบกันกระสุน นั่นคือมันจะไม่ทำผิดพลาดไม่ว่าจะเกิดอะไรขึ้น
Shahbaz

1
@ olibre ขอบคุณสำหรับรายละเอียดตัวอย่างชื่นชมมากและช่วยให้ฉันเริ่มต้นอย่างรวดเร็ว ในlib/Makefileตัวอย่างเช่นtargetจะใช้สำหรับการเทียบ.so .dllตัวอย่างขนานสำหรับapp/makefileจะเป็นประโยชน์สำหรับempty stringVS .exeเปรียบเทียบชื่อไฟล์แอป ตัวอย่างเช่นฉันมักจะไม่เห็นapp.exeระบบปฏิบัติการ Unix เหมือน ;-)

1
LSF? LFS? สะกดผิด?
แฟรงคลินหยู

19

ฉันเพิ่งทดลองเพื่อตอบคำถามนี้ฉันถามตัวเอง นี่คือข้อสรุปของฉัน:

เนื่องจากใน Windows คุณไม่สามารถมั่นใจได้ว่าunameคำสั่งนั้นพร้อมใช้งานคุณสามารถgcc -dumpmachineใช้ได้ สิ่งนี้จะแสดงเป้าหมายของคอมไพเลอร์

อาจมีปัญหาเมื่อใช้unameหากคุณต้องการรวบรวมข้ามบางอย่าง

นี่คือรายการตัวอย่างของผลลัพธ์ที่เป็นไปได้ของgcc -dumpmachine:

  • mingw32
  • i686-PC-Cygwin
  • x86_64-RedHat ลินุกซ์

คุณสามารถตรวจสอบผลลัพธ์ใน makefile ดังนี้:

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

มันทำงานได้ดีสำหรับฉัน แต่ฉันไม่แน่ใจว่าเป็นวิธีที่เชื่อถือได้ในการรับชนิดของระบบ อย่างน้อยก็น่าเชื่อถือเกี่ยวกับMinGWและนั่นคือทั้งหมดที่ฉันต้องการเพราะมันไม่จำเป็นต้องมีunameคำสั่งหรือแพ็คเกจMSYSใน Windows

เพื่อสรุปผลunameช่วยให้คุณมีระบบในการที่คุณกำลังรวบรวมและgcc -dumpmachineช่วยให้คุณมีระบบสำหรับการที่คุณกำลังรวบรวม


นี่เป็นจุดที่ดี อย่างไรก็ตามยังไม่ได้unameมาด้วยMinGWใช่ไหม อย่างไรก็ตามข้อความเพิ่มเติมเกี่ยวกับการรวบรวมข้ามเป็นสิ่งที่ดี
Shahbaz

2
@Shahbaz MinGW ตั้งค่าสามารถติดตั้ง MSYS (ซึ่งมี uname) แต่มันเป็นตัวเลือก ยังคงเป็นไปได้ที่จะค้นหาระบบที่มีเครื่องมือ gcc MinGW เท่านั้น
phsym

1
ที่ไม่ทำงานทุกที่เสียงดังกราวเป็นคอมไพเลอร์เริ่มต้นเช่น OS X และ FreeBSD
MarcusJ

@SebastianGodelet @MarcusJ $(shell $(CC) -dumpmachine)แก้ไขง่ายคือ ในฐานะของ OS X Sierra คำสั่ง -dumpmachine ทำงานบน Clang
Vortico

บน OS X 10.12.5 มันx86_64-apple-darwin16.6.0และว่างานสภาพอากาศที่คุณเรียกว่าgcc, ccหรือclangแต่ไม่ได้cl
MarcusJ

17

คอมไพล์ Makefileมีตัวอย่างมากมายของวิธีการจัดการโดยไม่ต้อง autoconf / automake ยังคงทำงานเกี่ยวกับความหลากหลายของแพลตฟอร์ม unixy


13
รู้ว่า Git ไม่ได้ใช้ Autofools อย่างใดทำให้ฉันรู้สึกเป็นธรรมในความเกลียดชังของฉันกับพวกเขา ...
Dan Molding

11
"Autofools"? นั่นเป็นคำที่พิมพ์ผิดโดยเจตนาหรือไม่? :)
JesperE

6
มันเป็น แต่ในความคิดที่สองฉันคิดว่าฉันชอบ "Autostools" ดีกว่า : D
Dan Molding

Btw คุณหมายถึงอะไร "พวกเขา"? คน Git หรือ Autotools : D
JesperE

8
ภาษาอังกฤษเป็นภาษาที่ไม่แม่นยำ เกี่ยวกับสิ่งนี้: if (!usesAutotools(git)) aversionTo(autotools) = justified;ฉันจะอธิบายให้ชัดเจนด้วยว่ามันเป็นเพียงเครื่องมือที่ฉันเกลียดชัง ฉันแน่ใจว่าคน Autotools เป็นคนดี
Dan Molding

11

อัปเดต: ฉันคิดว่าคำตอบนี้ล้าสมัยแล้ว ฉันโพสต์โซลูชั่นที่สมบูรณ์แบบใหม่ลงไปอีก

หาก makefile ของคุณอาจทำงานบน Windows ที่ไม่ใช่ Cygwin unameอาจไม่สามารถใช้งานได้ นั่นเป็นเรื่องที่น่าอึดอัดใจ แต่นี่เป็นทางออกที่เป็นไปได้ คุณต้องตรวจสอบ Cygwin ก่อนเพื่อออกกฎเนื่องจากมี WINDOWS ในPATHตัวแปรสภาพแวดล้อมด้วย

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

นี่เป็นสิ่งที่ดีในตอนนี้! คุณบอกฉันได้ไหม ฉันไม่ได้ใช้ Cygwin แต่ฉันได้ติดตั้ง MinGW ด้วยเส้นทาง bin ของมันใน PATH ถ้าฉันออกunameจากเทอร์มินัล cmd ปกติมันให้ฉัน MINGW สิ่งที่ฉันหมายถึงคือฉันยังมีunameโดยไม่ต้องใช้ Cygwin ฉันยังมี git bash แต่ฉันไม่ได้ลอง uname (ตอนนี้ฉันอยู่ใน Linux) คุณบอกฉันได้หรือไม่ว่าทั้งสองวิธีสามารถรวมอยู่ในรหัสของคุณได้อย่างไร
Shahbaz

หากคุณแน่ใจว่ามีชื่อพร้อมใช้งานนั่นเป็นทางออกที่ดีที่สุด แต่ในสภาพแวดล้อมของฉันทุกคนใช้ Windows และมีคนไม่กี่คนที่ติดตั้งcygwinหรือmingwดังนั้นฉันจึงไม่รับประกันว่าสิ่งใดก็ตามแม้จะเป็นมาตรฐานเหมือน uname ก็ตาม ขณะนี้ฉันมีปัญหากับโค้ดข้างต้นที่ใช้ make.exe ในเชลล์ cmd Windows เป็นแพลตฟอร์มที่น่าผิดหวังอย่างมากสำหรับการทำงานกับ
Ken Jackson

สิ่งที่ฉันหมายถึงคือก่อนทดสอบการมีอยู่ของ Windows ใน PATH คุณต้องแน่ใจว่าคุณไม่ได้ติดต่อกับ cygwin คุณจะแน่ใจได้อย่างไรว่าคุณไม่ได้ติดต่อกับ MinGW? ตัวอย่างเช่นเป็นไปได้ไหมใน Makefile ที่จะทดสอบว่าสามารถรันคำสั่งได้หรือไม่และถ้าunameไม่สามารถรันได้เราจะเข้าใจว่าเราอยู่ใน Windows หรือไม่
Shahbaz

ฉันกำลังพยายามหาวิธีแก้ปัญหาที่สะอาดสำหรับ Mingw / cygwin / shell-or-cmd / Linux เช่นกัน ในตอนท้ายของวันบางอย่างเช่น premake หรือ cmake ดูเหมือนเป็นความคิดที่ดีที่สุด
Isaac Nequittepas

นี่ไม่ใช่ทางออกที่ดีที่สุดอีกต่อไป โซลูชันใหม่ที่ฉันโพสต์ให้เห็นความแตกต่างของ Windows พื้นเมืองโดยมองหา ';' ในตัวแปร PATH โดยไม่มีการเรียกเชลล์
เคนแจ็กสัน

7

นั่นคืองานที่automake / autoconfของ GNU ออกแบบมาเพื่อแก้ปัญหา คุณอาจต้องการตรวจสอบพวกเขา

อีกทางหนึ่งคุณสามารถตั้งค่าตัวแปรสภาพแวดล้อมบนแพลตฟอร์มที่แตกต่างกันและทำให้คุณ Makefile มีเงื่อนไขกับพวกเขา


11
ฉันไม่แนะนำให้ใช้ automake / autoconf อย่างยิ่ง พวกเขาน่าเบื่อที่จะใช้เพิ่มค่าใช้จ่ายจำนวนมากในไฟล์ของคุณเพื่อสร้างเวลาของคุณ พวกเขาเพียงเพิ่มความซับซ้อนสำหรับเอฟเฟกต์น้อยมาก (ยังไม่มีการพกพาระหว่างระบบ)
Johannes Overmann

1
ฉันใช้เวลาสองสามวันในการเรียนรู้ที่makeจะทำสิ่งที่ฉันต้องการ ตอนนี้ฉันต้องการเป็น automake / autoconf ด้วยหรือไม่? - ไม่ สิ่งที่สามารถทำได้ใน makefile อย่างแน่นอนควรทำใน makefile ถ้าเพียงเพื่อที่ฉันจะไม่ได้มีจุดหยุดหลายจุดทุกครั้งที่ฉันต้องการแก้ไขการรวบรวม & ลิงค์
วิศวกร

makefiles ของคุณรองรับกี่แพลตฟอร์ม automake และ autoconf เป็นของตัวเองจริงๆเมื่อคุณต้องการพกพาไปยังหลาย ๆ แพลตฟอร์ม
Douglas Leeder

2
ฉันไม่ต้องการการพึ่งพาที่ไร้ประโยชน์และเปลี่ยนระบบ build ทั้งหมดของฉันเพียงเพื่อค้นหาว่า OS ใดที่ถูกรวบรวมไว้
MarcusJ

7

ในที่สุดฉันก็พบทางออกที่สมบูรณ์แบบที่แก้ปัญหานี้ให้ฉันได้

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

ตัวแปร UNAME ถูกตั้งค่าเป็น Linux, Cygwin, MSYS, Windows, FreeBSD, NetBSD (หรือ Solaris, Darwin, OpenBSD, AIX, HP-UX) หรือไม่รู้จัก จากนั้นสามารถเปรียบเทียบได้ทั่วทั้งส่วนที่เหลือของ Makefile เพื่อแยกตัวแปรและคำสั่งที่มีความอ่อนไหวต่อระบบปฏิบัติการ

กุญแจสำคัญคือ Windows ใช้เซมิโคลอนเพื่อแยกพา ธ ในตัวแปร PATH ในขณะที่คนอื่น ๆ ใช้โคลอน (เป็นไปได้ที่จะสร้างไดเรกทอรี Linux ด้วย ';' ในชื่อและเพิ่มลงใน PATH ซึ่งจะทำลายสิ่งนี้ แต่ใครจะทำสิ่งนั้น?) นี่น่าจะเป็นวิธีที่มีความเสี่ยงน้อยที่สุดในการตรวจจับ Windows พื้นเมืองเพราะมัน ไม่ต้องการการเรียกเชลล์ Cygwin และ MSYS PATH ใช้โคลอนเพื่อให้unameถูกเรียกใช้

โปรดทราบว่าตัวแปรสภาพแวดล้อมระบบปฏิบัติการสามารถใช้ตรวจจับ Windows แต่ไม่สามารถแยกความแตกต่างระหว่าง Cygwin และ Windows ดั้งเดิมได้ การทดสอบการสะท้อนของคำพูดใช้งานได้ แต่ต้องใช้การเรียก shell

น่าเสียดายที่ Cygwin เพิ่มข้อมูลรุ่นบางอย่างลงในunameดังนั้นฉันจึงเพิ่มการเรียก 'patsubst' เพื่อเปลี่ยนเป็น 'Cygwin' นอกจากนี้ uname สำหรับ MSYS จริง ๆ แล้วมีสามเอาต์พุตที่เป็นไปได้เริ่มต้นด้วย MSYS หรือ MINGW แต่ฉันใช้ patsubst เพื่อแปลงทั้งหมดเป็นเพียง 'MSYS'

หากจำเป็นต้องแยกแยะระหว่างระบบ Windows ดั้งเดิมที่มีและไม่มี uname.exe บนพา ธ บรรทัดนี้สามารถใช้แทนการกำหนดอย่างง่าย:

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

แน่นอนในทุกกรณี GNU ทำให้จำเป็นต้องมีหรืออีกยี่ห้อที่รองรับฟังก์ชั่นที่ใช้


6

ฉันพบปัญหาในวันนี้และฉันต้องการมันบน Solaris ดังนั้นนี่คือวิธีมาตรฐาน POSIX ในการทำสิ่งนี้

#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
รับข้อผิดพลาดบน OSX: "Makefile: 22: *** ตัวแยกขาดหายไปหยุด" ในบรรทัดนี้: "- @ make $ (UNAME_S)"
Czarek Tomczak

OSX น่าจะไม่เข้ากันดังนั้นให้ลองทำตามลำดับ (1) ตรวจสอบให้แน่ใจว่าคุณใช้ TAB เป็นตัวอักษรตัวแรกในบรรทัด (2) ลบเครื่องหมาย "- @" ที่หน้าเครื่องหมาย (2a) หาก 2 ใช้งานได้ลองอักขระหนึ่งตัวจากนั้นลองอีกหนึ่งตัว (3) ตรวจสอบให้แน่ใจว่า กำหนด UNAME_S แล้วลอง echo $ (UNAME_S) แทน - @ make $ (UNAME_S)
Huckle

6

นี่เป็นวิธีง่ายๆที่ตรวจสอบว่าคุณอยู่ในสภาพแวดล้อม Windows หรือ posix-like (Linux / Unix / Cygwin / Mac):

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

มันใช้ประโยชน์จากข้อเท็จจริงที่ว่า echo มีอยู่ทั้งในสภาพแวดล้อมแบบ posix และ Windows และใน Windows เชลล์ไม่ได้กรองเครื่องหมายคำพูด


1
ค่อนข้างไม่ปลอดภัยเนื่องจาก$PATHอาจอ้างถึงสิ่งอื่นecho(เหมืองทำ ... )
yyny

@YoYoYonnY ทำไมเส้นทางของคุณอ้างถึงเสียงสะท้อนอื่น ดูเหมือนว่าเป็นสถานการณ์ที่ไม่น่าเป็นไปได้
ซามูเอล

1
ไม่จริง ๆ แล้วคอมไพล์ทำมัน mingw ทำมัน cygwin ทำ ... และฉันก็ใส่ C: \ Windows \ System32 ที่ด้านล่างของเส้นทางของฉัน
yyny

1
"โซลูชัน" "นี้ใช้งานได้" สำหรับทุกสภาพแวดล้อม แต่ประเด็นของฉันคือว่านี่ไม่สามารถตรวจจับหน้าต่างได้อย่างปลอดภัย หากฉันต้องการตั้งค่า-mwindowsสถานะหรือเลือกระหว่าง.dllหรือ.soสิ่งนี้จะล้มเหลว
yyny

1
@YoYoYonnY ขอบคุณสำหรับการชี้แจง ในสถานการณ์ของฉันฉันใส่ใจถ้าฉันอยู่ในสภาพแวดล้อม Cygwin หรือ Windows หรือ Linux มากกว่าสิ่งที่ฉันเป็นในระบบปฏิบัติการดังนั้นนี่จึงเป็นประโยชน์สำหรับฉัน เสียงเหมือนความต้องการของคุณแตกต่างจากสิ่งที่ฉันเป็น
ซามูเอล

3

โปรดทราบว่า Makefiles ไวต่อการเว้นวรรคมาก นี่คือตัวอย่างของ Makefile ที่รันคำสั่งพิเศษใน OS X และใช้งานได้กับ OS X และ Linux โดยรวมแล้ว autoconf / automake เป็นหนทางที่จะไปหาอะไรที่ไม่สำคัญ

UNAME: = $ (shell uname -s)
CPP = g ++
CPPFLAGS = -pthread -ansi -Wall -Werror -pedantic -O0 -g3 -I / nexopia / include
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 thread.o utility.o vor.o

ทั้งหมด: vor

สะอาด
    rm -f $ (OBJECTS) หรือ

vor: $ (OBJECTS)
    $ (CPP) $ (LDFLAGS) -o vor $ (OBJECTS)
ifeq ($ (UNAME), ดาร์วิน)
    # ตั้งค่าตำแหน่งไลบรารี Boost
    install_name_tool -change libboost_system.dylib /nexopia/lib/libboost_system.dylib vor
endif

% .o:% .cpp $ (ส่วนหัว) Makefile
    $ (CPP) $ (CPPFLAGS) -c $

2

อีกวิธีในการทำเช่นนี้คือการใช้สคริปต์ "กำหนดค่า" หากคุณกำลังใช้งานกับ makefile อยู่แล้วคุณสามารถใช้ uname และ sed เพื่อรวมเอาสิ่งต่างๆ ก่อนอื่นในสคริปต์ของคุณให้ทำ:

UNAME=uname

จากนั้นเพื่อที่จะวางสิ่งนี้ใน Makefile ของคุณเริ่มต้นด้วย Makefile.in ซึ่งควรมีลักษณะดังนี้

UNAME=@@UNAME@@

ในนั้น.

ใช้คำสั่ง sed ต่อไปนี้ในสคริปต์กำหนดค่าของคุณหลังจากUNAME=unameบิต

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

ตอนนี้ makefile ของคุณควรมีการUNAMEกำหนดตามที่ต้องการ หากข้อความ / elif / else เป็นของเหลืออยู่!


คนแรกไม่ควรถือเป็นอย่างนี้หรือ UNAME = $ (uname)
Ken Jackson

0

ฉันมีกรณีที่ฉันต้องตรวจสอบความแตกต่างระหว่างสองรุ่นของ Fedora เพื่อปรับ
แต่งตัวเลือกบรรทัดคำสั่งสำหรับ inkscape: - ใน Fedora 31, inkscape เริ่มต้นคือ 1.0beta ที่ใช้--export-file
- ใน Fedora <31, inkscape เริ่มต้นคือ 0.92 ซึ่งใช้--export-pdf

Makefile ของฉันมีดังต่อไปนี้

# 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)=$@

งานนี้เพราะ/etc/os-releaseมีสาย

VERSION_ID=<value>

ดังนั้นคำสั่งของเชลล์ใน Makefile ส่งกลับสตริงVERSION_ID=<value>แล้วคำสั่ง EVAL VERSION_IDทำหน้าที่เกี่ยวกับเรื่องนี้เพื่อตั้งค่าตัวแปร เห็นได้ชัดว่าสามารถปรับแต่งสำหรับระบบปฏิบัติการอื่นขึ้นอยู่กับวิธีการจัดเก็บข้อมูลเมตา โปรดทราบว่าใน Fedora ไม่มีตัวแปรสภาพแวดล้อมเริ่มต้นที่ให้รุ่น OS มิฉะนั้นฉันจะใช้มัน!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.