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所做的复杂工作,您可以使用两个简单的技巧来检测操作系统:
- 环境variables
OS
- 然后命令
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_OS
select相关的东西:
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的示例 )。
下面我提供一个使用make
和gcc
来构build一个共享库的完整示例: *.so
或*.dll
具体取决于平台。 这个例子尽可能的简单一些:-)
要在Windows上安装make
和gcc
,请参阅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不会过滤引号。