OS检测makefile

我经常在几台不同的计算机和几种不同的操作系统上工作,这些操作系统是Mac OS X,Linux或Solaris。 对于我正在处理的项目,我从远程git存储库中提取代码。

我喜欢能够在我的项目上工作,无论我在哪个terminal。 到目前为止,我已经find了通过每次切换计算机时更改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 -ly 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 

这里已经有很多很好的答案,但是我想分享一个更完整的例子:a)不假设uname存在于Windows上; b)也检测处理器。 这里定义的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 

不带参数的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 

如果您不需要Git LSF所做的复杂工作,您可以使用两个简单的技巧来检测操作系统:

  • 环境variablesOS
  • 然后命令uname -s
 ifeq ($(OS),Windows_NT) detected_OS := Windows else detected_OS := $(shell uname -s) endif 

或者更安全的方法,如果不是Windows和命令uname不可用:

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

然后你可以根据detected_OSselect相关的东西:

 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 -s重要性的详细答案,而不是uname -o

OS (而不是uname -s )的使用简化了识别algorithm。 你仍然可以使用uname -s但是你必须处理if/else块来检查所有的MinGW / Cygwin / …变体。

注意:在任何Windows平台上,环境variablesOS始终设置为"Windows_NT" (请参阅维基百科上的Windows环境variables )。 OS一个替代scheme是环境variablesMSVC (它检查MS Visual Studio的存在,请参阅使用MSVC的示例 )。


下面我提供一个使用makegcc来构build一个共享库的完整示例: *.so*.dll具体取决于平台。 这个例子尽可能的简单一些:-)

要在Windows上安装makegcc ,请参阅Cygwin或MinGW 。

我的例子是基于5个文件

  ├── 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指出的“ 自动variables”文档 。

源代码

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

构build

修复Makefile的复制粘贴(用一个制表符replace前导空格)。

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

make命令在两个平台上都是一样的。 给定的输出是类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' 

运行

应用程序需要知道共享库在哪里。

在Windows上,一个简单的解决scheme是复制应用程序所在的库:

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

在类Unix操作系统上,可以使用LD_LIBRARY_PATH环境variables:

 > export LD_LIBRARY_PATH=lib 

在Windows上运行命令:

 > app/app.exe hello 

在类Unix操作系统上运行命令:

 > app/app hello 

为了回答这个问题,我最近正在试验我自问。 这是我的结论:

由于在Windows中,您不能确定uname命令是否可用,您可以使用gcc -dumpmachine 。 这将显示编译器目标。

如果你想做一些交叉编译,使用uname也可能有问题。

以下是gcc -dumpmachine可能输出的示例列表:

  • mingw32的
  • i686的-PC-的cygwin
  • x86_64的-红帽Linux的

你可以像这样在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 

它对我来说很好,但我不确定这是获取系统types的可靠方法。 至lessMinGW是可靠的,这就是我所需要的,因为它不需要在Windows中有uname命令或MSYS包。

总而言之, uname为您提供了编译的系统, gcc -dumpmachine为您提供了正在编译的系统。

希望它帮助了一个人。

git makefile包含了很多不使用autoconf / automake进行pipe理的例子,但仍可以在许多unixy平台上运行。

如果您的makefile可能在非cygwin Windows上运行,则uname可能不可用。 这很尴尬,但这是一个潜在的解决scheme。 你必须首先检查Cygwin来排除它,因为它的PATH中也有WINDOWS。

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

这是GNU的automake / autoconf被devise来解决的工作。 你可能想要调查他们。

或者,您可以在不同的平台上设置环境variables,并使您的Makefile有条件。

我今天遇到了这个问题,我在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 

请注意,Makefiles对间距非常敏感。 下面是一个在OSX上运行额外命令并在OSX和Linux上运行的Makefile的例子。 总的来说,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

 vor:$(OBJECTS)
     $(CPP)$(LDFLAGS)-o vor $(OBJECTS)
 ifeq($(UNAME),达尔文)
     #设置boost库的位置
     install_name_tool -change libboost_system.dylib /nexopia/lib/libboost_system.dylib vor
万一

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

另一种方法是使用“configure”脚本。 如果你已经使用了你的makefile,你可以使用uname和sed的组合来解决问题。 首先,在您的脚本中,执行:

 UNAME=uname 

然后,为了把这个放在你的Makefile中,从Makefile.in开始,它应该有类似的东西

 UNAME=@@UNAME@@ 

在里面。

UNAME=uname位后,在configuration脚本中使用以下sed命令。

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

现在你的makefile应该具有所需的UNAME定义。 如果/ elif / else语句都剩下了!

下面是一个简单的解决scheme,用于检查您是在Windows还是类似POSIX的环境(Linux / Unix / Cygwin / Mac)

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

它利用了posix-like和Windows环境中都存在echo的事实,而在Windows中,shell不会过滤引号。