天天看点

浅析OpenNI2---Build System 与KDE

Build system 与KDE

上一篇已经介绍到直接采用make就可以编译OpenNI2,这篇文章主要介绍下OpenNI2的makefile文件以及Linux下KDE集成开发环境的使用;

.

├── Makefile

├── Samples

│ ├── SimpleRead

│ │ └── Makefile

│ └── SimpleViewer

│ … └── Makefile

├── Source

│ ├── Core

│ │ └── Makefile

│ └── Drivers

│ … ├── DummyDevice

│ … │ └── Makefile

│ … ├── Kinect

│ … ├── OniFile

│ … │ └── Makefile

│ … ├── PS1080

│ … │ └── Makefile

│ … └── PSLink

│ … … └── Makefile

└── ThirdParty

… └── PSCommon

… ├── BuildSystem

… … ├── CommonCppMakefile

… … ├── CommonCSMakefile

… … ├── CommonDefs.mak

… … ├── CommonJavaMakefile

… … ├── CommonTargets.mak

… … ├── Platform.Arm

… … ├── Platform.x64

… … └── Platform.x86

顶层Makefile用于编译整个工程暂时先不去分析,我们先看下Sample目录下的Makefile

# SimpleViewer Makefile
include ../../ThirdParty/PSCommon/BuildSystem/CommonDefs.mak

BIN_DIR = ../../Bin
INC_DIRS = \
    ../../Include \
    ../../ThirdParty/GL/ \
    ../Common

SRC_FILES = *.cpp

ifeq ("$(OSTYPE)","Darwin")
    CFLAGS += -DMACOS
    LDFLAGS += -framework OpenGL -framework GLUT
else
    CFLAGS += -DUNIX -DGLX_GLXEXT_LEGACY
    USED_LIBS += glut GL
endif

USED_LIBS += OpenNI2

EXE_NAME = SimpleViewer

CFLAGS += -Wall

include ../../ThirdParty/PSCommon/BuildSystem/CommonCppMakefile
           

上面的各个变量都显而易见

- INC_DIRS 为头件目录

- SRC_FILES为源文件

- USED_LIBS为编译时需要链接的库

- EXE_NAME为生成可执行文件的名称

该文件主要定义EXE_NAME来表示该文件用于生成可执行文件,下面我们来看看CommonDefs.mak与CommonCppMakefile

# CommonDefs.mak
ifndef _COMMON_DEFS_MAKE
_COMMON_DEFS_MAKE=

# some defaults
ifndef CFG
    CFG = Release
endif

# find out the platform on which we're running
MACHINE = $(shell uname -m)
ifneq (,$(findstring x86_64,$(MACHINE)))
    HOST_PLATFORM = x64
else ifneq (,$(findstring x86,$(MACHINE)))
    HOST_PLATFORM = x86
else ifneq (,$(findstring i686,$(MACHINE)))
    HOST_PLATFORM = x86
else ifneq (,$(findstring i386,$(MACHINE)))
    HOST_PLATFORM = x86
else ifneq (,$(findstring arm,$(MACHINE)))
    HOST_PLATFORM = Arm
else
    DUMMY:=$(error Can't determine host platform)
endif

# now check if this is a cross-compilation or not
ifeq "$(PLATFORM)" ""
    PLATFORM = $(HOST_PLATFORM)
else
    ifneq "$(PLATFORM)" "$(HOST_PLATFORM)"
        # cross compiling. Take CXX and STAGING_DIR from environment
        PLATFORM_UPPER = $(shell echo $(PLATFORM) | tr 'a-z' 'A-Z')
        DUMMY:=$(eval CXX = $($(PLATFORM_UPPER)_CXX))
        DUMMY:=$(eval TARGET_SYS_ROOT = $($(PLATFORM_UPPER)_STAGING))

        ifeq "$(and $(CXX), $(TARGET_SYS_ROOT))" ""
            DUMMY:=$(error Cross-Compilation error. Can't find $(PLATFORM_UPPER)_CXX and $(PLATFORM_UPPER)_STAGING)
        endif
    endif
endif

# expand file list
SRC_FILES_LIST = $(wildcard $(SRC_FILES))

# define the intermediate directory
INT_DIR = $(BIN_DIR)/Intermediate/$(PLATFORM)-$(CFG)/$(OUTPUT_NAME)

# define output directory
OUT_DIR = $(BIN_DIR)/$(PLATFORM)-$(CFG)

# full path to output file
OUTPUT_FILE = $(OUT_DIR)/$(OUTPUT_NAME)

# take this file's dir
COMMON_MAK_DIR = $(dir $(lastword $(MAKEFILE_LIST)))

# get the OS type
OSTYPE := $(shell uname -s)

# platform specific args
include $(COMMON_MAK_DIR)Platform.$(PLATFORM)

endif # _COMMON_DEFS_MAKE_
           
# CommonCppMakefile
#############################################################################
# Primesense template makefile.
# This file should not be made, but only included from other makefiles.
# By default, this makefile compiles in release. To compile a debug version:
#    make CFG=Debug
#
# Project makefile should define the following BEFORE including this file:
# SRC_FILES - a list of all source files
# Output name under one of the following:
#     EXE_NAME (executable), 
#     LIB_NAME (dynamic library) or 
#     SLIB_NAME (static library) or
# BIN_DIR - Bin directory (output dir)
# INC_DIRS - a list of additional include directories
# LIB_DIRS - a list of additional library directories
# USED_LIBS - a list of libraries to link with
# DEFINES - [Optional] additional preprocessor defines
# CFLAGS - [Optional] additional flags for the compiler
# LDFLAGS - [Optional] additional flags for the linker
# SSE_GENERATION - [Optional] The SSE generation to use (default is 3)
# TARGET_SYS_ROOT - [Optional] The path to the root of the target
# ALLOW_WARNINGS - [Optional] Allow warnings during compilation
#############################################################################

# take this file's dir
COMMON_CPP_MAKE_FILE_DIR = $(dir $(lastword $(MAKEFILE_LIST)))

include $(COMMON_CPP_MAKE_FILE_DIR)CommonDefs.mak

# define a function to figure .o file for each source file (placed under intermediate directory)
SRC_TO_OBJ = $(addprefix ./$(INT_DIR)/,$(addsuffix .o,$(notdir $(basename $1))))

# create a list of all object files
OBJ_FILES = $(call SRC_TO_OBJ,$(SRC_FILES_LIST))

# define a function to translate any source file to its dependency file (note that the way we create
# dep files, as a side affect of compilation, always puts the files in the INT_DIR with suffix .d)
SRC_TO_DEP = $(addprefix ./$(INT_DIR)/,$(addsuffix .d,$(notdir $(basename $1))))

# create a list of all dependency files
DEP_FILES = $(call SRC_TO_DEP,$(SRC_FILES_LIST))

# older version of gcc doesn't support the '=' symbol in include dirs, so we replace it ourselves with sysroot
INC_DIRS_FROM_SYSROOT = $(patsubst =/%,$(TARGET_SYS_ROOT)/%,$(INC_DIRS))

# append the -I switch to each include directory
INC_DIRS_OPTION = $(foreach dir,$(INC_DIRS_FROM_SYSROOT),-I$(dir))

# append the -L switch to each library directory
LIB_DIRS_OPTION = $(foreach dir,$(LIB_DIRS),-L$(dir)) -L$(OUT_DIR)

# append the -l switch to each library used
USED_LIBS_OPTION = $(foreach lib,$(USED_LIBS),-l$(lib))

# append the -D switch to each define
DEFINES_OPTION = $(foreach def,$(DEFINES),-D$(def))

# tell compiler to use the target system root
ifdef TARGET_SYS_ROOT
    CFLAGS += --sysroot=$(TARGET_SYS_ROOT)
    LDFLAGS += --sysroot=$(TARGET_SYS_ROOT)
endif

# set Debug / Release flags
ifeq "$(CFG)" "Debug"
    CFLAGS += -O0 -g
endif
ifeq "$(CFG)" "Release"
    CFLAGS += -O2 -DNDEBUG
endif

CFLAGS += $(INC_DIRS_OPTION) $(DEFINES_OPTION)
CFLAGS += -fPIC -fvisibility=hidden

ifneq "$(ALLOW_WARNINGS)" "1"
    CFLAGS += -Werror
endif

LDFLAGS += $(LIB_DIRS_OPTION) $(USED_LIBS_OPTION)

# some lib / exe specifics
ifneq "$(LIB_NAME)" ""
    OUTPUT_NAME = lib$(LIB_NAME).so
    ifneq ("$(OSTYPE)","Darwin")
        LDFLAGS += -Wl,--no-undefined
        OUTPUT_NAME = lib$(LIB_NAME).so
        OUTPUT_COMMAND = $(CXX) -o $(OUTPUT_FILE) $(OBJ_FILES) $(LDFLAGS) -shared
    else
        LDFLAGS += -undefined error
        OUTPUT_NAME = lib$(LIB_NAME).dylib
        OUTPUT_COMMAND = $(CXX) -o $(OUTPUT_FILE) $(OBJ_FILES) $(LDFLAGS) -dynamiclib -headerpad_max_install_names -install_name $(OUTPUT_NAME)
    endif
endif
ifneq "$(EXE_NAME)" ""
    OUTPUT_NAME = $(EXE_NAME)
    # We want the executables to look for the .so's locally first:
    LDFLAGS += -Wl,-rpath ./
    OUTPUT_COMMAND = $(CXX) -o $(OUTPUT_FILE) $(OBJ_FILES) $(LDFLAGS)
endif
ifneq "$(SLIB_NAME)" ""
    OUTPUT_NAME = lib$(SLIB_NAME).a

    ifneq ("$(OSTYPE)","Darwin")
        OUTPUT_COMMAND = $(AR) -cq $(OUTPUT_FILE) $(OBJ_FILES)
    else
        OUTPUT_COMMAND = libtool -static -o $(OUTPUT_FILE) $(OBJ_FILES)
    endif
endif

define CREATE_SRC_TARGETS
# create a target for the object file (the CXX command creates both an .o file
# and a .d file)
ifneq ("$(OSTYPE)","Darwin")
$(call SRC_TO_OBJ,$1) : $1 | $(INT_DIR)
    $(CXX) -MD -MP -MT "$(call SRC_TO_DEP,$1) [email protected]" -c $(CFLAGS) -o $$@ $$<
else
$(call SRC_TO_OBJ,$1) : $1 | $(INT_DIR)
    $(CXX) -x c++ -c $(CFLAGS) -o $$@ $$<
endif
endef

#############################################################################
# Targets
#############################################################################
.PHONY: clean-objs clean-defs

include $(COMMON_CPP_MAKE_FILE_DIR)CommonTargets.mak

# create targets for each source file
$(foreach src,$(SRC_FILES_LIST),$(eval $(call CREATE_SRC_TARGETS,$(src))))

# include all dependency files (we don't need them the first time, so we can use -include)
-include $(DEP_FILES)

$(OUTPUT_FILE): $(OBJ_FILES)
    $(OUTPUT_COMMAND)

clean-objs:
    rm -rf $(OBJ_FILES)

clean-defs:
    rm -rf $(DEP_FILES)

clean: clean-objs clean-defs
           

CommonDefs.mak主要定义了平台、编译器以及目录

CommonCppMakefile描述了主要的make过程

当我们使用OpenNI的build system编写makefile时仅需对output name和头文件路径链接库进行描述

例如下面的Makefile用于编译libOpenNI2.so,这个文件中output name 为LIB_NAME = OpenNI2

include ../../ThirdParty/PSCommon/BuildSystem/CommonDefs.mak

BIN_DIR = ../../Bin

INC_DIRS = \
    ../../Include \
    ../../ThirdParty/PSCommon/XnLib/Include \
    ../Drivers/OniFile/Formats \
    ../../ThirdParty/LibJPEG

SRC_FILES = \
    *.cpp \
    ../Drivers/OniFile/Formats/XnCodec.cpp \
    ../Drivers/OniFile/Formats/XnStreamCompression.cpp \
    ../../ThirdParty/LibJPEG/*.c \

ifeq ("$(OSTYPE)","Darwin")
    INC_DIRS += /opt/local/include
    LIB_DIRS += /opt/local/lib
    LDFLAGS += -framework CoreFoundation -framework IOKit
endif

LIB_NAME = OpenNI2

LIB_DIRS = ../../ThirdParty/PSCommon/XnLib/Bin/$(PLATFORM)-$(CFG)
USED_LIBS = XnLib dl pthread
ifneq ("$(OSTYPE)","Darwin")
        USED_LIBS += rt  
endif

DEFINES += OPENNI2_EXPORT

CFLAGS += -Wall

include ../../ThirdParty/PSCommon/BuildSystem/CommonCppMakefile
           

下面在看下top Makefile

#############################################################################
# OpenNI makefile.
# 
# default configuration is Release. for a debug version use:
#   make CFG=Debug
#
# default compiler is g++. for another one use:
#   make CXX=<comp>
#
# By default, CLR projects will only be build if mono is installed.
# To force CLR projects use:
#   make FORCE_BUILD_CLR=1
#
#############################################################################

include ThirdParty/PSCommon/BuildSystem/CommonDefs.mak

MAJOR_VERSION = $(shell grep "define ONI_VERSION_MAJOR" Include/OniVersion.h | cut -f )
MINOR_VERSION = $(shell grep "define ONI_VERSION_MINOR" Include/OniVersion.h | cut -f )
MAINT_VERSION = $(shell grep "define ONI_VERSION_MAINT" Include/OniVersion.h | cut -f )

ifeq ("$(OSTYPE)","Darwin")
    OS_NAME = MacOSX
else
    OS_NAME = Linux
endif
PRODUCT_STRING = OpenNI-$(OS_NAME)-$(PLATFORM)-$(shell cd Packaging && python -c "import UpdateVersion; print UpdateVersion.getVersionName()" && cd ..)
FINAL_DIR = Packaging/Final

OPENNI = Source/Core
XNLIB  = ThirdParty/PSCommon/XnLib/Source
DEPTH_UTILS = Source/DepthUtils

# list all drivers
ALL_DRIVERS = \
    Source/Drivers/DummyDevice   \
    Source/Drivers/PS1080 \
    Source/Drivers/PSLink \
    Source/Drivers/OniFile

# list all wrappers
ALL_WRAPPERS = \
    Wrappers/java/OpenNI.jni \
    Wrappers/java/OpenNI.java 

# list all tools
ALL_TOOLS = \
    Source/Drivers/PS1080/PS1080Console \
    Source/Drivers/PSLink/PSLinkConsole

# list all core projects
ALL_CORE_PROJS = \
    $(XNLIB)  \
    $(OPENNI) \
    $(DEPTH_UTILS) \
    $(ALL_DRIVERS) \
    $(ALL_WRAPPERS) \
    $(ALL_TOOLS)

# list all samples
CORE_SAMPLES = \
    Samples/SimpleRead \
    Samples/EventBasedRead \
    Samples/MultipleStreamRead \
    Samples/MWClosestPoint \
    Samples/MWClosestPointApp 

# list all java samples
JAVA_SAMPLES = \
    Samples/SimpleViewer.java   

ifeq "$(GLUT_SUPPORTED)" "1"
    ALL_TOOLS += \
        Source/Tools/NiViewer

    CORE_SAMPLES += \
        Samples/SimpleViewer \
        Samples/MultiDepthViewer \
        Samples/ClosestPointViewer
else
    ifeq "$(GLES_SUPPORTED)" "1"
        CORE_SAMPLES += 
    endif
endif

ALL_SAMPLES = \
    $(CORE_SAMPLES) \
    $(JAVA_SAMPLES)

# list all projects that are build
ALL_BUILD_PROJS = \
    $(ALL_CORE_PROJS) \
    $(ALL_SAMPLES)

ALL_PROJS = \
    $(ALL_BUILD_PROJS)

ALL_PROJS_CLEAN = $(foreach proj,$(ALL_PROJS),$(proj)-clean)

# define a function which creates a target for each proj
define CREATE_PROJ_TARGET
$1: 
    $$(MAKE) -C $1

$1-clean: 
    $$(MAKE) -C $1 clean
endef

################ TARGETS ##################

.PHONY: all $(ALL_PROJS) $(ALL_PROJS_CLEAN) install uninstall clean release

# make all makefiles
all: $(ALL_PROJS)

core: $(ALL_CORE_PROJS)

samples: $(ALL_SAMPLES)

# create projects targets
$(foreach proj,$(ALL_PROJS),$(eval $(call CREATE_PROJ_TARGET,$(proj))))

# additional dependencies
$(OPENNI):                            $(XNLIB)
Wrappers/java/OpenNI.jni:   $(OPENNI) $(XNLIB)

Source/Drivers/DummyDevice: $(OPENNI) $(XNLIB)
Source/Drivers/RawDevice:   $(OPENNI) $(XNLIB)
Source/Drivers/PS1080:      $(OPENNI) $(XNLIB) $(DEPTH_UTILS)
Source/Drivers/PS1080/PS1080Console: $(OPENNI) $(XNLIB)
Source/Drivers/PSLink:      $(OPENNI) $(XNLIB)
Source/Drivers/PSLink/PSLinkConsole: $(OPENNI) $(XNLIB)
Source/Drivers/OniFile:     $(OPENNI) $(XNLIB)

Source/Tools/NiViewer:      $(OPENNI) $(XNLIB)

Samples/SimpleRead:         $(OPENNI)
Samples/EventBasedRead:     $(OPENNI)
Samples/MultipleStreamRead: $(OPENNI)
Samples/MWClosestPoint:     $(OPENNI)
Samples/MWClosestPointApp:  $(OPENNI) Samples/MWClosestPoint

Samples/SimpleViewer:       $(OPENNI)
Samples/MultiDepthViewer:   $(OPENNI)
Samples/ClosestPointViewer: $(OPENNI) Samples/MWClosestPoint
Samples/SimpleViewer.java:            Wrappers/java/OpenNI.java

$(FINAL_DIR):
    mkdir -p $(FINAL_DIR)

doc:
    Source/Documentation/Runme.py
    rm -f Source/Documentation/html/*.md5

release: | all doc $(FINAL_DIR)
    Packaging/Harvest.py Packaging/$(PRODUCT_STRING) $(PLATFORM)
    cd Packaging; tar -cjf Final/$(PRODUCT_STRING).tar.bz2 $(PRODUCT_STRING)

# clean is cleaning all projects
clean: $(ALL_PROJS_CLEAN)
           

顶层的Makefile主要定义了OpenNI生成的所有的so库和samples ,然后通过make 的-C option依次进入各个子目录进行make操作

# make all makefiles
all: $(ALL_PROJS)

# create projects targets
$(foreach proj,$(ALL_PROJS),$(eval $(call CREATE_PROJ_TARGET,$(proj))))

# define a function which creates a target for each proj
define CREATE_PROJ_TARGET
$1: 
    $$(MAKE) -C $1

$1-clean: 
    $$(MAKE) -C $1 clean
endef
           

到此我们简单的分析了OpenNI2的build system,makefile写的十分清晰简洁,阅读理解都很轻松不得不佩服开发的大神,当然类似的build system也是有的,使用最多的应该视android 的build system,有兴趣的可以去看看;

在Linux下使用console进行代码编写编译是最高效的方式,不过难免有些人还是喜欢有图形界面的IDE工具,这里在介绍下Linux下一款IDE工具kdevelop,ubuntu下的安装命令

sudo apt-get install kdevelop

KDE的界面如下,后面介绍下如何使用KDE编译OpenNI2

浅析OpenNI2---Build System 与KDE
  1. 导入工程 Project –> Open/Import Project..
    浅析OpenNI2---Build System 与KDE
  2. 选择OpenNI2的代码路径,点击“Next”
    浅析OpenNI2---Build System 与KDE
  3. 在Build System中选择“Custom Makefile Project Manager”点击“Finish”完成工程导入
    浅析OpenNI2---Build System 与KDE
  4. 配置Project Project –> Open Configuration 在“Make”界面中输入编译命令和参数,完成后就可以进行编译了
    浅析OpenNI2---Build System 与KDE
  5. 运行调试 点击“Execute”,在弹出的对话框中选中工程OpenNI2,点击“Add New”,在Executable中选中需要调试的可执行文件,这样就可以直接在KDE中运行调试了
    浅析OpenNI2---Build System 与KDE

到目前为止还没有涉及代码,下面马上就要浅析下OpenNI2的代码架构了