프로그래밍/Linux/Ubuntu

.so 빌드와 테스트를 위한 makefile

초록생선 2017. 3. 6. 13:16

앞선 글을 읽고...

[프로그래밍/Linux/Ubuntu] - Linux(리눅스) Makefile 예제,강좌 (debug/release 경로 분리, rebuild, 자동 dependency)

이 글의 연속 선상입니다.

앞선 글에서는, 일반적인 /trunk/... 단위의 상대적인 경로 입력이 지원되지 않았습니다.

이번 글에서는 /trunk/... 단위의 상대적인 경로 입력이 지원되고,

.so 파일 빌드와 해당 테스트를 할 수 있는 빌드도 포함됩니다.

자세한 설명은 이전글을 참고 바라며,

.so 빌드를 위한 설정등은 해당 makefile을 참고하시기 바랍니다.


예제

libmodule.zip


빌드 & 테스트

압축을 $HOME, 즉, ~/trunk로 압축을 풀었다고 가정합니다.


cd ~/trunk/src/libmodule

make debug

make release

를 통해 libmodule.so의 debug/release를 빌드합니다.


cd ~/trunk/src/libmodule.test

make debug

make release

를 통해 libmoduletest.run의 debug/release를 빌드합니다.


cd ~/trunk/src/build/linux/native/release 혹은

cd ~/trunk/src/build/linux/native/debug

./libmoduletest.run

을 실행하면,

Success to load module
[a.cpp] hello
[b.cpp] world
[libmodule.cpp] say good-bye!
이 생성됩니다.


디렉토리 구조

tree 명령을 통해 확인된 디렉토리 구조는 아래와 같습니다.

./trunk
├── build
│   └── linux
│       └── native
│           ├── debug
│           │   ├── intermediate
│           │   │   ├── libmodule.so
│           │   │   │   ├── lib
│           │   │   │   │   ├── a.d
│           │   │   │   │   ├── a.o
│           │   │   │   │   ├── b.d
│           │   │   │   │   └── b.o
│           │   │   │   └── libmodule
│           │   │   │       ├── libmodule.d
│           │   │   │       ├── libmodule.o
│           │   │   │       ├── so_main.d
│           │   │   │       └── so_main.o
│           │   │   └── libmoduletest.run
│           │   │       └── libmodule.test
│           │   │           ├── test.d
│           │   │           └── test.o
│           │   ├── libmodule.so
│           │   └── libmoduletest.run
│           └── release
│               ├── intermediate
│               │   ├── libmodule.so
│               │   │   ├── lib
│               │   │   │   ├── a.d
│               │   │   │   ├── a.o
│               │   │   │   ├── b.d
│               │   │   │   └── b.o
│               │   │   └── libmodule
│               │   │       ├── libmodule.d
│               │   │       ├── libmodule.o
│               │   │       ├── so_main.d
│               │   │       └── so_main.o
│               │   └── libmoduletest.run
│               │       └── libmodule.test
│               │           ├── test.d
│               │           └── test.o
│               ├── libmodule.so
│               └── libmoduletest.run
└── src
    ├── inc
    │   └── libmodule.h
    ├── lib
    │   ├── a.cpp
    │   ├── a.h
    │   ├── b.cpp
    │   └── b.h
    ├── libmodule
    │   ├── ld_conf.lds
    │   ├── libmodule.cpp
    │   ├── Makefile
    │   └── so_main.cpp
    └── libmodule.test
        ├── Makefile
        └── test.cpp
 

* src 쪽 경로는 아무 변경이 없습니다.

* .d와 .o 파일은 intermediate 쪽으로 격리되어 저장됩니다.


특징

.so 빌드시, 즉, ./trunk/src/libmodule/Makefile의 POST_BUILD step에,

$(STRIP) $(TARGET);

가 포함되어 있습니다.

그래서, so 빌드에 불필요한 심볼 정보등은 제거됩니다.


또한, ./trunk/src/libmodule/ld_conf.lds에,

"libmodule_*"로 시작되는 함수만 export 가능하도록 하였습니다.

그래서, 만일, Export할 API는 항상 libmodule_로 시작하도록 가정해야 합니다.


.so Makefile

###############################
# Makefile ver 0.1 by greenfish
###############################

####################
# basic make command
####################

# make                           : debug build   (native)
# make debug                     : debug build   (native)
# make release                   : release build (native)
# make rebuild [debug, release]  : rebuild       (native)
# make clean [debug, release]    : clean

#############################
# set default compile options
#############################

CC         = gcc
STRIP      = strip
PLATFORM   = i686
CONFIG     = debug
LIBNAME    = libmodule.so
LIBCOMPILE = -fPIC
LIBLINK    = -shared -Wl,-soname,$(LIBNAME),--version-script=ld_conf.lds
TRUNK_DIR  = $(abspath ../..)
SRC_DIR    = $(TRUNK_DIR)/src
INC_DIR    = -I./ -I$(SRC_DIR)/inc -I$(SRC_DIR)/lib -I/usr/local/include
BUILD_DIR  = $(TRUNK_DIR)/build/linux/$(PLATFORM)/$(CONFIG)
TARGET     = $(BUILD_DIR)/$(LIBNAME)
CFLAGS     = -Wall $(INC_DIR)
LFLAGS     = -ldl
SYSROOT    =

####################################
# get makefile command line argument
####################################

ifneq "$(findstring clean, $(MAKECMDGOALS))" ""
    ARG.CLEAN = 1
endif
ifneq "$(findstring release, $(MAKECMDGOALS))" ""
    ARG.RELEASE = 1
endif
ifneq "$(findstring debug, $(MAKECMDGOALS))" ""
    ARG.DEBUG = 1
endif
ifneq "$(findstring rebuild, $(MAKECMDGOALS))" ""
    ARG.REBUILD = 1
endif

#####################################
# DEBUG / RELEASE build option branch
#####################################

ifeq ($(ARG.RELEASE),1)
    # -----------------
    # for release build
    # -----------------
    CFLAGS    += -DNDEBUG -O2
    CONFIG    = release
else
    # ---------------
    # for debug build
    # ---------------
    CFLAGS    += -g -DDEBUG -O0
    CONFIG    = debug
endif

# ----------------------------------
# for native build (using local gcc)
# ----------------------------------
# example) 2.6.38-8-generic-i686
# in cygwin, uname -r has bracket form (ex, 2.7.0(0.306/5/3)),
#    replace bracket to _ to correct error "... near unexpected token `('"
KERNEL_RELEASE = $(shell uname -r)
DELIMETER      = -
MACHINE_TYPE   = $(shell uname -m)
BRACKET_LEFT   = (
BRACKET_RIGHT  = )
PLATFORM_TMP   = $(KERNEL_RELEASE)$(DELIMETER)$(MACHINE_TYPE)
PLATFORM_TMP1  = $(subst $(BRACKET_LEFT),_,$(PLATFORM_TMP))
PLATFORM       = native

##################
# makefile process
##################

.PHONY: debug release build clean rebuild PRE_BUILD POST_BUILD all

# make process scenario
BUILD_STEP = PRE_BUILD $(TARGET) POST_BUILD

# set makefile target and dependency
# to prevent "make: Nothing to be done for 'release'" warning,
# use @true keyword
ifeq ($(ARG.REBUILD),1)
    # under rebuild, do clean before build
    rebuild: | clean $(BUILD_STEP)
    debug: ; @true
    release: ; @true
else ifeq ($(ARG.CLEAN),1)
    # under clean, target has no rule to build
    release: ; @true
    debug: ; @true
else
    # under build release or debug, do build
    build: | $(BUILD_STEP)
    release: build
    debug: build
endif

#######
# macro
#######

CONVERT_SRC   = $(subst $(LIBMODULE.INTERMEDIATE_DIR),$(SRC_DIR),$(@:.o=.cpp))
CONVERT_BUILD = $(subst $(SRC_DIR),$(LIBMODULE.INTERMEDIATE_DIR),$(@:.o=.cpp))

###################
# compile file list
###################

# main source
SRC_MAIN  = libmodule/so_main.cpp
SRC_MAIN += libmodule/libmodule.cpp

# common library
SRC_LIB   = lib/a.cpp
SRC_LIB  += lib/b.cpp

# all compile source file list
LIBMODULE.TARGET = $(SRC_MAIN) $(SRC_LIB)

# compile meta-file to be in intermediate directory
LIBMODULE.INTERMEDIATE_DIR = $(BUILD_DIR)/intermediate/$(LIBNAME)

# compile meta-file list (.obj, .d)
LIBMODULE.OBJ = $(addprefix $(LIBMODULE.INTERMEDIATE_DIR)/, $(LIBMODULE.TARGET:.cpp=.o))
LIBMODULE.DEP = $(addprefix $(LIBMODULE.INTERMEDIATE_DIR)/, $(LIBMODULE.TARGET:.cpp=.d))

###########
# Link Part
###########

$(TARGET): $(LIBMODULE.OBJ)
    @echo ----------------------------------------
    @echo Link : $(TARGET)
    @echo ----------------------------------------
    $(CC) $(LFLAGS) $(LIBLINK) $(LIBMODULE.OBJ) -o $(TARGET) $(SYSROOT)
    $(info )

##############
# Compile Part
##############

$(LIBMODULE.OBJ): %.o:
    @echo ----------------------------------------
    @echo Compile : $(notdir $(CONVERT_BUILD))
    @echo ----------------------------------------
    @mkdir -p $(@D)
    @$(CC) -MM -MF $(@:.o=.d) -MT"$(@)" $(CFLAGS) $(CONVERT_SRC) $(SYSROOT)
    $(CC) $(CFLAGS) $(LIBCOMPILE) -c -o $@ $(CONVERT_SRC) $(SYSROOT)
    $(info )

###################
# Pre-build process
###################

PRE_BUILD:
    @echo ================================================================
    @echo Make file started. config =\> $(CONFIG), platform =\> $(PLATFORM)
    @echo ================================================================
   
####################
# Post-build process
####################

# after release build, do strip command
POST_BUILD:
    @echo Post build...
    @if [ "$(CONFIG)" = "release" ]; then \
        echo Start to strip; \
        echo $(STRIP) $(TARGET); \
        $(STRIP) $(TARGET); \
    fi;
    @echo Compile completed : $(TARGET)
    @echo ================================================================
    @echo Make file finished. config =\> $(CONFIG), platform =\> $(PLATFORM)
    @echo ================================================================
   
##################################
# Clean up 한다.
clean:
    @rm -f $(LIBMODULE.OBJ)
    @rm -f $(LIBMODULE.DEP)
    @rm -f $(TARGET)
    @echo -----------------------------------------------------------------
    @echo Clean work finished. config =\> $(CONFIG), platform =\> $(PLATFORM)
    @echo -----------------------------------------------------------------

##################################
# 끝 부분에 dependency 파일들을 include한다.
-include $(LIBMODULE.DEP)
 


.run(실행 파일) Makefile

 ###############################
# Makefile ver 0.1 by greenfish
###############################

####################
# basic make command
####################

# make                           : debug build   (native)
# make debug                     : debug build   (native)
# make release                   : release build (native)
# make rebuild [debug, release]  : rebuild       (native)
# make clean [debug, release]    : clean

#############################
# set default compile options
#############################

CC         = gcc
STRIP      = strip
PLATFORM   = i686
CONFIG     = debug
LIBNAME    = libmoduletest.run
LIBCOMPILE =
LIBLINK    =
TRUNK_DIR  = $(abspath ../..)
SRC_DIR    = $(TRUNK_DIR)/src
INC_DIR    = -I./ -I$(SRC_DIR)/inc -I$(SRC_DIR)/lib -I/usr/local/include
BUILD_DIR  = $(TRUNK_DIR)/build/linux/$(PLATFORM)/$(CONFIG)
TARGET     = $(BUILD_DIR)/$(LIBNAME)
CFLAGS     = -Wall $(INC_DIR)
LFLAGS     = -ldl
SYSROOT    =

####################################
# get makefile command line argument
####################################

ifneq "$(findstring clean, $(MAKECMDGOALS))" ""
    ARG.CLEAN = 1
endif
ifneq "$(findstring release, $(MAKECMDGOALS))" ""
    ARG.RELEASE = 1
endif
ifneq "$(findstring debug, $(MAKECMDGOALS))" ""
    ARG.DEBUG = 1
endif
ifneq "$(findstring rebuild, $(MAKECMDGOALS))" ""
    ARG.REBUILD = 1
endif

#####################################
# DEBUG / RELEASE build option branch
#####################################

ifeq ($(ARG.RELEASE),1)
    # -----------------
    # for release build
    # -----------------
    CFLAGS    += -DNDEBUG -O2
    CONFIG    = release
else
    # ---------------
    # for debug build
    # ---------------
    CFLAGS    += -g -DDEBUG -O0
    CONFIG    = debug
endif

# ----------------------------------
# for native build (using local gcc)
# ----------------------------------
# example) 2.6.38-8-generic-i686
# in cygwin, uname -r has bracket form (ex, 2.7.0(0.306/5/3)),
#    replace bracket to _ to correct error "... near unexpected token `('"
KERNEL_RELEASE = $(shell uname -r)
DELIMETER      = -
MACHINE_TYPE   = $(shell uname -m)
BRACKET_LEFT   = (
BRACKET_RIGHT  = )
PLATFORM_TMP   = $(KERNEL_RELEASE)$(DELIMETER)$(MACHINE_TYPE)
PLATFORM_TMP1  = $(subst $(BRACKET_LEFT),_,$(PLATFORM_TMP))
PLATFORM       = native

##################
# makefile process
##################

.PHONY: debug release build clean rebuild PRE_BUILD POST_BUILD all

# make process scenario
BUILD_STEP = PRE_BUILD $(TARGET) POST_BUILD

# set makefile target and dependency
# to prevent "make: Nothing to be done for 'release'" warning,
# use @true keyword
ifeq ($(ARG.REBUILD),1)
    # under rebuild, do clean before build
    rebuild: | clean $(BUILD_STEP)
    debug: ; @true
    release: ; @true
else ifeq ($(ARG.CLEAN),1)
    # under clean, target has no rule to build
    release: ; @true
    debug: ; @true
else
    # under build release or debug, do build
    build: | $(BUILD_STEP)
    release: build
    debug: build
endif

#######
# macro
#######

CONVERT_SRC   = $(subst $(LIBMODULE.INTERMEDIATE_DIR),$(SRC_DIR),$(@:.o=.cpp))
CONVERT_BUILD = $(subst $(SRC_DIR),$(LIBMODULE.INTERMEDIATE_DIR),$(@:.o=.cpp))

###################
# compile file list
###################

# main source
SRC_MAIN  = libmodule.test/test.cpp

# common library
SRC_LIB   =

# all compile source file list
LIBMODULE.TARGET = $(SRC_MAIN) $(SRC_LIB)

# compile meta-file to be in intermediate directory
LIBMODULE.INTERMEDIATE_DIR = $(BUILD_DIR)/intermediate/$(LIBNAME)

# compile meta-file list (.obj, .d)
LIBMODULE.OBJ = $(addprefix $(LIBMODULE.INTERMEDIATE_DIR)/, $(LIBMODULE.TARGET:.cpp=.o))
LIBMODULE.DEP = $(addprefix $(LIBMODULE.INTERMEDIATE_DIR)/, $(LIBMODULE.TARGET:.cpp=.d))

###########
# Link Part
###########

$(TARGET): $(LIBMODULE.OBJ)
    @echo ----------------------------------------
    @echo Link : $(TARGET)
    @echo ----------------------------------------
    $(CC) $(LIBLINK) $(LIBMODULE.OBJ) -o $(TARGET) $(SYSROOT) $(LFLAGS)
    $(info )

##############
# Compile Part
##############

$(LIBMODULE.OBJ): %.o:
    @echo ----------------------------------------
    @echo Compile : $(notdir $(CONVERT_BUILD))
    @echo ----------------------------------------
    @mkdir -p $(@D)
    @$(CC) -MM -MF $(@:.o=.d) -MT"$(@)" $(CFLAGS) $(CONVERT_SRC) $(SYSROOT)
    $(CC) $(CFLAGS) $(LIBCOMPILE) -c -o $@ $(CONVERT_SRC) $(SYSROOT)
    $(info )

###################
# Pre-build process
###################

PRE_BUILD:
    @echo ================================================================
    @echo Make file started. config =\> $(CONFIG), platform =\> $(PLATFORM)
    @echo ================================================================
    
####################
# Post-build process
####################

# after release build, do strip command
POST_BUILD:
    @echo Post build...
    @echo Compile completed : $(TARGET)
    @echo ================================================================
    @echo Make file finished. config =\> $(CONFIG), platform =\> $(PLATFORM)
    @echo ================================================================
    
##################################
# Clean up 한다.
clean:
    @rm -f $(LIBMODULE.OBJ)
    @rm -f $(LIBMODULE.DEP)
    @rm -f $(TARGET)
    @echo -----------------------------------------------------------------
    @echo Clean work finished. config =\> $(CONFIG), platform =\> $(PLATFORM)
    @echo -----------------------------------------------------------------

##################################
# 끝 부분에 dependency 파일들을 include한다.
-include $(LIBMODULE.DEP)