ระบบตรวจจับ 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.