rdtsc

What is rdtsc

  • rdtsc is ReaD TimeStamp Count
  • limition
    • only work on x86 platform

simple implement use for x86_64 platform

uint64_t rdtsc()
{
   uint32_t hi, lo;
   __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
   return ( (uint64_t)lo)|( ((uint64_t)hi)<<32 );
}

rdtsc.h

#ifndef __RDTSC_H_DEFINED__
#define __RDTSC_H_DEFINED__


#if defined(__i386__)

static __inline__ unsigned long long rdtsc(void)
{
  unsigned long long int x;
     __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
     return x;
}
#elif defined(__x86_64__)

static __inline__ unsigned long long rdtsc(void)
{
  unsigned hi, lo;
  __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
  return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}

#elif defined(__powerpc__)

static __inline__ unsigned long long rdtsc(void)
{
  unsigned long long int result=0;
  unsigned long int upper, lower,tmp;
  __asm__ volatile(
                "0:                  \n"
                "\tmftbu   %0           \n"
                "\tmftb    %1           \n"
                "\tmftbu   %2           \n"
                "\tcmpw    %2,%0        \n"
                "\tbne     0b         \n"
                : "=r"(upper),"=r"(lower),"=r"(tmp)
                );
  result = upper;
  result = result<<32;
  result = result|lower;

  return(result);
}

#else

#error "No tick counter is available!"

#endif


/*  $RCSfile:  $   $Author: kazutomo $
 *  $Revision: 1.6 $  $Date: 2005/04/13 18:49:58 $
 */

#endif

Sample one

#include <stdio.h>
#include "rdtsc.h"

int main(int argc, char* argv[])
{
  unsigned long long a,b;

  a = rdtsc();
  b = rdtsc();

  printf("%llu\n", b-a);
  return 0;
}

Sample two

#include <stdio.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>

#include "rdtsc.h"

#define N (1024*1024*2)

int main(int argc, char* argv[])
{
  unsigned long long a,b;
  unsigned long long min,max;
  char* p;
  int i;

  p = (char*)malloc(N);
  assert( p!=(char*)0 );

  max = 0;
  min = UINT64_MAX;

  for(i=0; i<N; i++ ) {
    a = rdtsc();
    p[i] = 0;
    b = rdtsc() - a;
    if( b > max ) max = b;
    else if( b < min ) min = b;
  }
  printf("min=%llu\n", min);
  printf("max=%llu\n", max);
  return 0;
}

TODO: translate CPU cycle to time

  • time_in_seconds = number_of_clock_cycles / frequency
  • cat /proc/cpuinfo |grep 'MHz'
  • gettimeofday and rdtsc

Something need to consider?

  • Context switch?
  • Multi-core processors

gettimeofday

#include <iostream>
#include <sys/time.h> // for gettimeofday()
using namespace std;

int main()
{
    struct timeval t1, t2;
    double elapsedTime;

    // start timer
    gettimeofday(&t1, NULL);

    // do something
    // ...

    // stop timer
    gettimeofday(&t2, NULL);

    // compute and print the elapsed time in millisec
    elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0;    // sec to ms
    elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0; // us to ms
    cout << elapsedTime << " ms.\n";

    return 0;
}

gettimeofday vs rdtsc, source code

#include <iostream>
#include <sys/time.h> // for gettimeofday()
#include <stdlib.h> // for malloc
#include <string.h> // for memcpy
#include "rdtsc.h"

// cat /proc/cpuinfo |grep 'MHz'
#define CPUMHZ 1200.0f
// frequency per micro second
#define CPU_SPEED_US (CPUMHZ*1024*1024/1000/1000)

using namespace std;
int main()
{
    // test malloc
    cout << "malloc:\n";
    struct timeval t1, t2;
    double elapsedTime;
    gettimeofday(&t1, NULL);
    char* p = (char*)malloc(4);
    gettimeofday(&t2, NULL);
    free(p);
    elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000000.0; // sec to us
    elapsedTime += (t2.tv_usec - t1.tv_usec) ;
    cout << "\t" << elapsedTime << " us (by gettimeofday)\n";

    unsigned long long a,b;
    a = rdtsc();
    char* q = (char*)malloc(4);
    b = rdtsc() - a;
    free(q);
    cout << "\ttimestamp count is " << b << ", elapsedTime " << b/CPU_SPEED_US << " us\n";

    // test memcpy
    cout << "memcpy:\n";
    char tmp1[1024] = {0};
    char tmp2[1024] = {'a'};
    int count = 1000;

    gettimeofday(&t1, NULL);
    for (int i=0; i<count; i++) {
        memcpy(tmp1, tmp2, sizeof(tmp1));
    }
    gettimeofday(&t2, NULL);
    elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000000.0; // sec to us
    elapsedTime += (t2.tv_usec - t1.tv_usec) ;
    cout << "\t" << elapsedTime << " us (by gettimeofday)\n";

    a = rdtsc();
    for (int i=0; i<count; i++) {
        memcpy(tmp1, tmp2, sizeof(tmp1));
    }
    b = (rdtsc()-a)/count;
    cout << "\ttimestamp count is " << b << ", elapsedTime " << b/CPU_SPEED_US << " us\n";
    return 0;
}

output

dennis@dennis:~/tt$ g++ -O3 -o t t.cc
dennis@dennis:~/tt$ ./t
malloc:
    0 us (by gettimeofday)
    timestamp count is 58, elapsedTime 0.0460943 us
memcpy:
    0 us (by gettimeofday)
    timestamp count is 0, elapsedTime 0 us
dennis@dennis:~/tt$ g++ -O2 -o t t.cc
dennis@dennis:~/tt$ ./t
malloc:
    0 us (by gettimeofday)
    timestamp count is 69, elapsedTime 0.0548363 us
memcpy:
    0 us (by gettimeofday)
    timestamp count is 0, elapsedTime 0 us
dennis@dennis:~/tt$ g++ -O1 -o t t.cc
dennis@dennis:~/tt$ ./t
malloc:
    0 us (by gettimeofday)
    timestamp count is 58, elapsedTime 0.0460943 us
memcpy:
    2 us (by gettimeofday)
    timestamp count is 3, elapsedTime 0.00238419 us
dennis@dennis:~/tt$ g++ -o t t.cc
dennis@dennis:~/tt$ ./t
malloc:
    80 us (by gettimeofday)
    timestamp count is 1198, elapsedTime 0.952085 us
memcpy:
    102 us (by gettimeofday)
    timestamp count is 295, elapsedTime 0.234445 us
dennis@dennis:~/tt$ g++ -g -o t t.cc
dennis@dennis:~/tt$ ./t
malloc:
    82 us (by gettimeofday)
    timestamp count is 1421, elapsedTime 1.12931 us
memcpy:

    timestamp count is 295, elapsedTime 0.234445 us

Reference

redis

Redis

Redis is an in-memory database that persist on disk. The data model is key-value,
but many different kind of values are supported: String, Lists, Sets, Sorted Set,
Hashes, HyperLogLog, Bitmaps.http://redis.io

Install

Use

  • Playing with Redis

    % cd src
    % ./redis-cli
    redis> ping
    PONG
    redis> set foo bar
    OK
    redis> get foo
    “bar”
    redis> incr mycounter
    (integer) 1
    redis> incr mycounter
    (integer) 2
    redis>

Analyze

  • size of source code, 1.3MB
  • total lines of code, 58185
  • allocate double memory size when append string

Reference

linux memory

process and memory

  • how process use memory
    There are 5 kinds of segment datas on process, which is code segment, data segment,
    BSS segment, heap segment and stack segment.

    • code, use for store execute instruction.
    • data, store global variable, which had been initialized.
    • BSS, uninitialized global variable, all would be setted to zero.
    • heap, memory be allocated dynamic
    • stack, use for store local variable
  • how these segments look like

    • |stack address … heap address … BSS … data … code|
    • stack address is on high address, and code is on low.
    • here is an example to tell the ture

      #include<stdio.h>
      #include<malloc.h>
      #include<unistd.h>
      int bss_var;
      int data_var0=1;
      int main(int argc,char **argv)
      {
        printf("below are addresses of types of process's mem\n");
        printf("Text location:\n");
        printf("\tAddress of main(Code Segment):%p\n",main);
        printf("____________________________\n");
        int stack_var0=2;
        printf("Stack Location:\n");
        printf("\tInitial end of stack:%p\n",&stack_var0);
        int stack_var1=3;
        printf("\tnew end of stack:%p\n",&stack_var1);
        printf("____________________________\n");
        printf("Data Location:\n");
        printf("\tAddress of data_var(Data Segment):%p\n",&data_var0);
        static int data_var1=4;
        printf("\tNew end of data_var(Data Segment):%p\n",&data_var1);
        printf("____________________________\n");
        printf("BSS Location:\n");
        printf("\tAddress of bss_var:%p\n",&bss_var);
        printf("____________________________\n");
        char *b = sbrk((ptrdiff_t)0);
        printf("Heap Location:\n");
        printf("\tInitial end of heap:%p\n",b);
        brk(b+4);
        b=sbrk((ptrdiff_t)0);
        printf("\tNew end of heap:%p\n",b);
        return 0;
      }
      
    • output as below
      below are addresses of types of process's mem
      Text location:
          Address of main(Code Segment):0x400606
      ____________________________
      Stack Location:
          Initial end of stack:0x7fff8b4d1b5c
          new end of stack:0x7fff8b4d1b58
      ____________________________
      Data Location:
          Address of data_var(Data Segment):0x601050
          New end of data_var(Data Segment):0x60104c
      ____________________________
      BSS Location:
          Address of bss_var:0x601058
      ____________________________
      Heap Location:
          Initial end of heap:0x203c000
          New end of heap:0x203c004
      
  • memory space of process
    Linux use virtual memory address to express the process memory space, it make
    all the process have isolate physic memory space, they will not interfere with
    each other.
  • process memory manager
    Use command cat /proc/[pid]/maps can show the memory information of process.
  • allocate and recover process memory
  • system physic memory manager

Reference

unit test

cppmock

gmock

exception

  • network exception, such as connection timeout, receive/send failure
    • connect refused
      • use exist IP, but port is not listened, then the connect operation will return error connect refused
      • hook connect function
      • iptables, iptables -t filter -p tcp -A OUTPUT -d DEST_IP --dport DEST_PORT -j REJECT --reject-with tcp-reset
    • connect timeout
    • read timeout
    • write timeout
    • disconnect when read/write
    • connect slowly
    • package dirty
  • memory exception, such as not enough memory, allocation failure
    • allocate fail
      • hook malloc function
  • disk exception, such as disk crash, not enough space
  • program exception

reference

spf13 vim

spf13 vim

  • spf13 vim
  • Installation on linux
  • Requires Git 1.7+ and Vim 7.3+
  • curl https://j.mp/spf13-vim3 -L > spf13-vim.sh && sh spf13-vim.sh

install

[dennis@localhost ~]$ curl https://j.mp/spf13-vim3 -L > spf13-vim.sh 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   142  100   142    0     0     79      0  0:00:01  0:00:01 --:--:--    79
  0     0    0     0    0     0      0      0 --:--:--  0:00:03 --:--:--     0
100  5214  100  5214    0     0   1036      0  0:00:05  0:00:05 --:--:--  6998
[dennis@localhost ~]$ sh spf13-vim.sh 
Attempting to back up your original vim configuration.
‘/home/dennis/.vim’ -> ‘/home/dennis/.vim.20151007_1444202174’
‘/home/dennis/.vimrc’ -> ‘/home/dennis/.vimrc.20151007_1444202174’
Trying to update spf13-vim
Cloning into '/home/dennis/.spf13-vim-3'...
remote: Counting objects: 3303, done.
remote: Total 3303 (delta 0), reused 0 (delta 0), pack-reused 3303
Receiving objects: 100% (3303/3303), 3.16 MiB | 16.00 KiB/s, done.
Resolving deltas: 100% (1485/1485), done.
Checking connectivity... done.
[✔] Successfully cloned spf13-vim.
[✔] Setting up vim symlinks.
Trying to update vundle
Cloning into '/home/dennis/.vim/bundle/vundle'...
remote: Counting objects: 2941, done.
remote: Total 2941 (delta 0), reused 0 (delta 0), pack-reused 2941
Receiving objects: 100% (2941/2941), 870.60 KiB | 19.00 KiB/s, done.
Resolving deltas: 100% (1012/1012), done.
Checking connectivity... done.
[✔] Successfully cloned vundle.
[✔] Now updating/installing plugins using Vundle

Thanks for installing spf13-vim.
© 2015 http://vim.spf13.com/

[dennis@localhost ~]$ ls .vim/bundle/
auto-pairs           tagbar                    vim-less
ctrlp-funky          tlib_vim                  vim-litecorrect
ctrlp.vim            undotree                  vim-markdown
fonts                vim-abolish               vim-multiple-cursors
gist-vim             vim-addon-mw-utils        vim-nerdtree-tabs
HTML-AutoCloseTag    vim-airline               vim-over
matchit.zip          vim-bufferline            vim-php-namespace
neocomplete.vim      vim-coffee-script         vim-preview
neosnippet           vim-coloresque            vim-rails
neosnippet-snippets  vim-colors                vim-repeat
nerdcommenter        vim-colorschemes          vim-signify
nerdtree             vim-colors-solarized      vim-snippets
PIV                  vim-commentary            vim-surround
pythoncomplete       vim-css3-syntax           vim-textobj-indent
python_match.vim     vim-cucumber              vim-textobj-quote
python-mode          vim-cucumber-align-pipes  vim-textobj-sentence
python.vim           vim-easymotion            vim-textobj-user
restore_view.vim     vim-fugitive              vim-toml
rust.vim             vim-haml                  vim-twig
salt-vim             vim-indent-guides         vim-wordy
sessionman.vim       vim-javascript            vundle
syntastic            vim-json                  webapi-vim
tabular              vim-jst                   wildfire.vim
[dennis@localhost ~]$ du .vim -sh
125M    .vim
[dennis@localhost ~]$ tar zcf tab.tar.gz .vim
[dennis@localhost ~]$ ls -lh tab.tar.gz 
-rw-rw-r--. 1 dennis dennis 67M Oct  7 17:07 tab.tar.gz

Plugin instroduce

  • Vundle The best plugin manager
  • NERDTree
    • quick start/close using ctrl+e
    • o open selected file
    • r refresh selected folder
    • C select root directory
    • m add/delete/move/copy
    • ctrl+h/l switch window to left or right
  • ctrlp
    • quick start/close with ctrl+p
  • Tagbar
    • quick start/close with <leader>tt
  • EasyMotion
    • ,,w move foreword
    • ,,b move backword
  • Surround managing all the ‘“[{}]”‘ etc, using :help surround for more
    • cs'" , change ' to "
    • ysiw" , add " around the word
    • ysiw[
    • ysiw<p>
    • ds' , delete ' around the word
  • neocwomplcache autocomplete++
  • Fugitive
    • <leader>gs :Gstatus
    • <leader>gd :Gdiff
    • <leader>gc :Gcommit
    • <leader>gb :Gblame
    • <leader>gl :Glog
    • <leader>gp :Git push
  • Tabularize align everything
    • <Leader>a= :Tabularize /=
    • <Leader>a: :Tabularize /:
    • <Leader>a:: :Tabularize /:\zs
    • <Leader>a, :Tabularize /,
    • <Leader>a<Bar> :Tabularize
  • NERDCommenter
    • allow you to wrangle your code comments.
    • <leader>c<space>
  • numbers.vim
    • better line numbers
  • Airline
    • provide a lightweight themable statusline with no external dependencies.
  • Syntastic integrated syntax checking

Weakness

  • More suitable for the front-end program (html and python)
  • Too many plugins, and too big (100+MB)
  • Ag better than Ack
  • Not include vimshell and vimproc

Advantage (just for me)

  • NERDTree
  • Tagbar
  • EasyMotion
  • Surround

More powerful plugin

YouCompleteMe install offline

Reference

How to export a specify full directory by shell file

Requirement

  • project files

    myprj
        build
          env.sh
          Makefile
          submake
            MakeFlagTest1
            MakeFlagTest2
        doc
          readme.doc
        src
          a.h
          a.c
          b.h
          b.c
          main.c
        test
          test.c
    
  • In env.sh, need to export some environment variable, how to implement?
    If add hard code, it will looks like : export PRJ_HOME=/home/dennis/myprj

  • Using case

    • cd /home/dennis; source myprj/build/env.sh
    • cd /home/dennis; source ./myprj/build/env.sh
    • cd /home/dennis; source ~/myprj/build/env.sh
    • cd /home/dennis/myprj/build; source env.sh
    • cd /home/dennis/myprj/build; source ./env.sh
    • cd /home/dennis/myUT/src; source ../../myprj/build/env.sh
    • cd /home/dennis/myUT/src; source ~/myprj/build/env.sh

Test 1, using export PRJ_HOME=$(dirname $(dirname $0))

  • Failure case, when on suse release version, will failed to do dirname $0,
    you will find that echo $0 output -bash, not bash expected.

Test 2, using export PRJ_HOME=$(dirname $(dirname ${BASH_SOURCE[0]))

  • Failure case, when using relative directory.

Other test

echo "working directory is " $(pwd)
echo "BASH_SOURCE is " ${BASH_SOURCE}
echo "BASH_SOURCE[0] is " ${BASH_SOURCE[0]}
echo "dirname BASH_SOURCE[0] is " $(dirname ${BASH_SOURCE[0]})
echo "2 dirname BASH_SOURCE[0] is " $(dirname $(dirname ${BASH_SOURCE[0]}))
PARENT_DIR=$(dirname $(dirname ${BASH_SOURCE[0]}))
echo "parent dir is " ${PARENT_DIR}
export GMDB_HOME=$(pwd ${PARENT_DIR})

TODO, find the effective method

  • If we can get the bash file name?
  • How to translate relative directory to be full directory?

Reference

makefile

Generate each source file to be one bin file

  • Makefile

    CC     = g++
    CFLAGS += -g -Wall
    SRC    = $(wildcard *.cc)
    #OBJS   = $(SRC:.cc=.o)
    BINS   = $(SRC:.cc=)
    LIBS   = 
    
    all:$(BINS)
    
    # bin files depend on *.cc files
    %:%.cc
        $(CC) $(CFLAGS) -o $@ $<
    
    .PHONY: clean
    clean:
        rm -f *.o $(BINS)
    

Generate all source files to be on bin file

  • Makefile

    CC     = g++
    CFLAGS += -g -Wall
    SRC    = $(wildcard *.cc)
    OBJS   = $(SRC:.cc=.o)
    #BINS   = $(SRC:.cc=)
    TARGET = test
    LIBS   = 
    
    all:$(TARGET)
    
    # bin files depend on *.cc files
    $(TARGET):$(OBJS)
        $(CC) $(CFLAGS) -o $@ $<
    
    .PHONY: clean
    clean:
        rm -f *.o $(TARGET)
    

Auto run all Makefile in all sub-directory

  • Makefile

    ########################################################
    # Makefile
    # History:
    #        2013/04/17 Dennis Create
    ########################################################
    
    include ./Make.defines
    
    subdirs := $(sort $(subst /,,$(dir $(wildcard */*))))
    
    all:
        $(foreach N,$(subdirs),make -C $(N);)
    
    .PHONY: clean
    clean:
        $(foreach N,$(subdirs),make -C $(N) clean;)
        rm -f $(CLEANFILES)
    
  • Make.define

    # "make" is executed in all the subdirectories of this directory
    
    CC = gcc
    CFLAGS  = -I../lib -g -o2 -D_REENTRANT -Wall
    LIBS = ../libunp.a -lpthread
    
    LIBUNP_NAME = ../libunp.a
    
    LIB_OBJS = error.o wrapsock.o str_echo.o writen.o wrapunix.o wraplib.o str_cli.o \
               readline.o wrapstdio.o tcp_listen.o tcp_connect.o signal.o readn.o \
               write_fd.o read_fd.o
    
    CLEANFILES = *.o *.a tags cscope.out
    
  • epoll/Makefile

    CC     = gcc
    CFLAGS = -Wall -g2
    LIB_CPP= -lstdc++
    
    TARGET = t1 t2 t3
    
    all:$(TARGET)
    
    t1: t1.c
        $(CC) $(FLAGS) -o $@ $^
    
    t2: t2.c
        $(CC) $(FLAGS) -o $@ $^
    
    t3: t3.cpp
        $(CC) $(FLAGS) $(LIB_CPP) -o $@ $^
    
    .PHONY:clean
    clean:
        rm -f *.o $(TARGET)
    

Example

  • Makefile

    ###########################################################
    # Makefile
    #
    # History:
    # 2013-06-03 Dennis Create
    # 2013-06-04 Dennis Make it correct and better
    ###########################################################
    
    .SUFFIXES : .x .o .c .cpp .s
    ROOTDIR    = $(shell pwd)
    
    CC = g++
    
    TARGET = cppscsi
    
    #LIBDIR    = $(ROOTDIR)/lib
    #SHARLIB:=$(shell find $(LIBDIR) -name '*.so')
    
    INCPATH = $(ROOTDIR)
    INCPRG  =-I$(INCPATH) \
             -I$(INCPATH)/common \
             -I$(INCPATH)/digest \
             -I$(INCPATH)/datasegment \
             -I$(INCPATH)/login
    
    INCDIR  = $(INCPRG)
    
    CFLAGS += $(INCDIR)
    
    #SOURCES:=$(shell find $(ROOTDIR) -name '*.cpp' -o -name '*.c')
    SOURCES:=$(shell find $(ROOTDIR) -maxdepth 1 -name '*.cpp' -o -name '*.c')
    CPPSRC :=$(notdir $(SOURCES))
    OBJECTS:=$(CPPSRC:.cpp=.o)
    
    # search all subdirs
    #subdirs := $(sort $(subst /,,$(dir $(wildcard */*))))
    subdirs := ./common \
               ./digest \
               ./data \
               ./datasegment \
               ./login
    
    # generate subdirs object name
    OBJS_OTHER_SRC := $(shell find $(subdirs) -name '*.cpp')
    OBJS_OTHER := $(OBJS_OTHER_SRC:.cpp=.o)
    
    all: $(TARGET)
    
    $(TARGET): $(OBJECTS)
        @echo "**************** build subdir ****************"
        $(foreach N,$(subdirs),make -C $(N);)
        @echo "**************** link target ****************"
        $(CC) -o $(TARGET) $(OBJECTS) $(OBJS_OTHER) $(SHARLIB) $(CFLAGS)
    
    $(OBJECTS): %.o:%.cpp
        $(CC) -c $< $(CFLAGS)
    
    .PHONY: clean
    clean:
        $(foreach N,$(subdirs),make -C $(N) clean;)
        rm -f *.o $(TARGET) 
    
  • login/Makefile

    ###########################################################
    # Makefile
    #
    # History:
    # 2013-06-05 Dennis Create
    ###########################################################
    
    CC= g++
    
    SRC= LoginRequestParser.cpp \
         LoginResponseParser.cpp \
         ISID.cpp
    
    FLAGS = -I../ -I../common
    
    OBJS := $(SRC:.cpp=.o)
    
    all: $(OBJS)
    
    $(OBJS): %.o:%.cpp
        $(CC) $(FLAGS) -c $<
    
    .PHONY: clean
    clean:
        rm -f *.o 
    

An complex example

  • Makefile

    ###########################################################
    # Makefile
    # 
    # History:
    #    v1.2  2014-08-24 Dennis  Generate install script
    #    v1.1  2014-07-12 Dennis  Auto generate version info
    #    v1.0  2014-06-19 Dennis  Create
    ###########################################################
    
    CC     = gcc
    CFLAGS+= -Wall -g
    INC    = -I./
    SRC    = main.c \
             other_sources.c
    OBJ    = $(SRC:.c=.o)
    # NOTE: gcc start loading libraries from the right, 
    #       so $(LIB) should be on the far right of gcc 
    LIB    = -lpthread
    TARGET = myapp
    
    all: gen-version $(TARGET)
    
    $(TARGET): $(OBJ)
        $(CC) $(CFLAGS) $(INC) -o $@ $^ $(LIB)
    
    gen-version:
        @./update-version.sh
    
    # Generate install script
    release:
        @echo "Generate release script"
        @tar zcf myapp.tar.gz myapp config.conf
        @cat install.sh myapp.tar.gz >myapp-install.run
        @chmod +x myapp-install.run
    
    .PHONY:clean
    clean:
        -rm -f $(TARGET) $(OBJ) myapp.tar.gz myapp-install.run
    
  • install.sh

    #!/bin/bash
    echo ""
    echo "Self Extracting Installer"
    echo ""
    
    export TMPDIR=`mktemp -d /tmp/selfextract.XXXXXX`
    
    ARCHIVE=`awk '/^__ARCHIVE_BELOW__/{print NR+1; exit 0;}' $0`
    
    tail -n+$ARCHIVE $0 | tar xzv -C $TMPDIR
    
    CDIR=`pwd`
    cd $TMPDIR
    
    #./installer   # 改为压缩包中安装程序的地址
    
    # Make sure we are root
    if [ `id | sed -e 's/(.*//'` != "uid=0" ]; then
      echo "Sorry, you need super user privileges to run this script."
      exit 1
    fi
    
    #UNINST=
    #
    # Check for and uninstall any previous version.
    #
    if [ -x "${UNINST}" ]; then
      echo "Removing previous installation..."
      #
    fi
    
    INSTALL_DIR=/opt/app/myapp/ 
    if [ ! -d $INSTALL_DIR ]; then
        mkdir -p $INSTALL_DIR
    fi
    cp -f myapp $INSTALL_DIR
    cp -f config.conf $INSTALL_DIR
    
    cd $CDIR
    rm -rf $TMPDIR
    
    exit 0
    
    __ARCHIVE_BELOW__
    
  • update-version.sh

    sh -c "sed -i -e '/__MYAPP_AUTO_VERSION__/ {
        s/=.*$/= \"git commit $(git rev-parse HEAD), compiled $(date '+%Y-%m-%d %H:%M')\"\;/
    }' version.c"
    sh -c "sed -i -e '/__MYAPP_AUTO_DATE__/ {
            s/=.*$/= \"$(date '+%Y')\"\;/
    }' version.c"
    
  • version.c

    /*===============================================================
     * Copyright (C) 2014 All rights reserved.
     * 
     * 文件名称:version.h
     * 创 建 者:Matrix207
     * 创建日期:2014年07月12日
     * 描    述:
     *
     * 更新日志:
     *
     ================================================================*/
    #ifndef _VERSION_H_
    #define _VERSION_H_
    
    char __MYAPP_AUTO_VERSION__[] = "git commit 93785a3926fd2be190a2154b639d8eb7a83fa2e3, compiled 2014-08-24 13:08";
    char __MYAPP_AUTO_DATE__[] = "2014";
    #endif /* _VERSION_H_ */
    

Another example

  • Makefile

    #################################################################
    # Copyright (C) 2014 All rights reserved.
    # 
    # 文件名称:Makefile
    # 创 建 者:Dennis
    # 创建日期:2014年12月11日
    # 描    述:
    #
    # 更新日志:
    #   v0.1  20141211  Dennis  创建
    ##################################################################
    DATE_TIME=$(shell date '+%y%m%d%H%M%S')
    DATE_TIME2=$(shell date '+%Y-%m-%d %H:%M')
    
    DIR_INC = ./common
    DIR_SRC = ./
    DIR_BIN = ./
    CC      = gcc
    INC     = -I./common -I./inc
    SRC     = $(wildcard ${DIR_SRC}/*.c)
    COMMON  = $(wildcard ${DIR_INC}/*.c)
    LIB     = -lm
    #LIB     = -lpthread
    CFLAGS  = -g -Wall -I${DIR_INC}
    
    TARGET = ${DIR_BIN}/mytest
    
    all: PREPARE_DIR gen-version $(TARGET)
    
    $(TARGET): $(SRC) $(COMMON)
        $(CC) $(CFLAGS) $(INC) -o $@ $^ $(LIB)
    
    PREPARE_DIR:
        mkdir -p $(DIR_BIN)
    
    gen-version:
        ./update-version.sh
    
    .PHONY:clean
    clean:
        rm -f $(TARGET)