sqlite3的进程安全访问测试

前言

想写个demo, 由C程序写sqlite3数据库中的表A, 由java程序去读sqlite3数据库中的表A.
这样,就涉及到sqlite3的线程安全和进程安全访问。
写好测试程序后,跑起来,很失望。
sqlite3同一个表无法由2个进程同时读写表记录。
总有一个进程会出现“database is locked”的报错,去查了资料。大概意思是:因为是文件型数据库,无法使2个进程同时向同一个文件写数据。我的应用是一个读(读完一条记录删一条),一个写,应该会触发sqlite的文件读写锁吧。
尝试sql执行失败后重试,倒是可以继续跑。但是每一个SQL都有可能失败,都要重试。这执行效率就下来了。
换其他数据库了,失望。
其实,sqlite作者是可以解决这个问题的。只需要封装sqlite3_xx的接口实现不去直接操作文件,而是由同一个数据库代理程序(第一次打开数据库时创建数据库代理程序的实例)访问数据库文件,这样,就可以达到多个进程共享一个数据库文件,数据库的打开和关闭,带上引用计数就好。改动的代码也不大。谁知道sqlite作者咋想的呢,哪个数据库的使用者没有多进程访问同一个数据库的需求呢?

如果使用者非要使用sqlite3, 来支持多进程安全访问。只能自己再封装一组接口,由自己的程序来控制数据库文件的访问(同一个数据库文件只有一个访问者,对使用者隐藏了数据库文件的操作). sqlite3自己可以保证线程安全性。这样也能进程安全,其实这活应该由sqlite3数据库来完成。如果使用者要想那么多,谁还来用这么难用的数据库。毕竟大部分数据库都是可以进程安全的。

说来说去,其实sqlite3需要加一个服务程序,那些sqlite3_xx接口,都是通过本地socket来向服务程序读写数据,这样就正常了。这样改动也不大。

也许sqlite的需求场景,就是给一个程序用吧。

demo下载点

src_test_sqlite_process_safe.7z

demo预览

# @file README

# 下载 sqlite-autoconf-3250200.tar.gz
# tar -xzvf sqlite-autoconf-3250200.tar.gz
# cd sqlite-autoconf-3250200
# ./configure ./configure --prefix /home/sqlite3_was_install
# make
# make install

# 将 /home/sqlite3_was_install 拷贝到工程的doc目录
// @file main.cpp
// @brief 测试sqlite3的进程安全性访问
// @note 
//		实验环境:
//				debian8.8
//		实验结论: 
//				sqlite3做的渣, 不支持多进程访问同一个数据库的同一个表中的记录
//				当多进程同时高频度读写同一个表时, 任何一句sql都可能执行失败, 失望
// 				当一句正常的sql执行失败时,总不能去无限的重试吧,那还叫数据库么?
// 				

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <errno.h>

#include "const_define.h"
#include "sqlite3.h"

void init(const char* psz_log_owner_name);
void uninit();
void proc_sig_term(int num);
int fn_test(int argc, char** argv);

// @ref https://www.tutorialspoint.com/sqlite/sqlite_c_cpp.htm
int fn_sqlite_db_init();
int fn_test_sqlite_read();
int fn_test_sqlite_write();

typedef int (*PFN_cb_sqlite_proc)(void* cb_info, int argc, char **argv, char **azColName);
int cb_sqlite_proc_select(void* cb_info, int argc, char **argv, char **azColName);
int cb_sqlite_proc_insert(void* cb_info, int argc, char **argv, char **azColName);

bool fn_open_db(sqlite3*& db);
void fn_close_db(sqlite3*& db);
void fn_free_sqlite_msg(char*& psz_msg);
bool fn_open_or_create_tbl(sqlite3* db, const char* psz_tbl_name, const char* psz_field_declare);

// sqlite sql example
// CREATE TABLE TBL_NAME(ID INT PRIMARY KEY NOT NULL,NAME TEXT NOT NULL,AGE INT NOT NULL,ADDRESS CHAR(50),SALARY REAL);
// INSERT INTO TBL_NAME(ID,NAME,AGE,ADDRESS,SALARY) VALUES (1, 'Paul', 32, 'California', 20000.00 );
// DELETE from TBL_NAME where ID=2;
// UPDATE TBL_NAME set SALARY = 25000.00 where ID=1;;
// SELECT * from TBL_NAME;

bool fn_sql_exec(
	sqlite3* db, 
	const char* psz_sql, 
	std::string& str_err_msg, 
	TAG_SQLITE3_CALLBACK_INFO* p_cb_info, 
	PFN_cb_sqlite_proc pfn_cb);

bool is_file_exist(const char* psz_file_path_name, int& i_err_code, std::string& str_err_msg);

int main(int argc, char** argv)
{
    char sz_buf[MAX_MSG_LENGTH] = {'\0'};

#ifdef MAKE_FILE_MACRO__BIN_NAME
    sprintf(sz_buf, "%s", MAKE_FILE_MACRO__BIN_NAME);
    init(sz_buf);
    MYLOG_D("MAKE_FILE_MACRO__BIN_NAME = [%s]\n", MAKE_FILE_MACRO__BIN_NAME);
#else
    init(NULL);
#endif // #ifdef MAKE_FILE_MACRO__BIN_NAME

    fn_test(argc, argv);
    uninit();

    MYLOG_D("THE END\n");
    return EXIT_SUCCESS;
}

void uninit()
{
}

void proc_sig_term(int num)
{
    MYLOG_D("SIGTERM = %d, num = %d\n", SIGTERM, num);
    MYLOG_D("maybe can do some clean task before quit\n");
    exit(1);	
}

void init(const char* psz_log_owner_name)
{
    int i = 0;

    // daemon(0, 0);

    // clear screen (print 25 empty line)
    for (i = 0; i < 25; i++) {
        MYLOG_D("\n");
    }

    signal(SIGTERM, proc_sig_term);
}

int fn_test(int argc, char** argv)
{
	bool b_rc = false;
    MYLOG_D(">> fn_test()\n");

	do {
		if (2 != argc) {
			break;
		}

		fn_sqlite_db_init();
		b_rc = true;
		switch (argv[1][0]) {
			case 'r':
				fn_test_sqlite_read();
				break;
			case 'w':
				fn_test_sqlite_write();
				break;
			default:
				b_rc = false;
				break;
		}

		if (!b_rc) {
			break;
		}

		b_rc = true;
	} while (0);

	if (!b_rc) {
		MYLOG_D("usage : this_program r(ead)/w(rite)\n");
		MYLOG_D("e.g. this_program r\n");
		MYLOG_D("e.g. this_program w\n");
	}

    return 0;
}

int cb_sqlite_proc_insert(void* cb_info, int argc, char **argv, char **azColName) {
	int i = 0;
	TAG_SQLITE3_CALLBACK_INFO* p_cb_info = (TAG_SQLITE3_CALLBACK_INFO*)cb_info;
	TAG_COL_NAME_AND_VALUE name_value;

	if (NULL != p_cb_info) {
		p_cb_info->clear();
		for(i = 0; i < argc; i++) {
			name_value.set(i, azColName[i], argv[i]);
			p_cb_info->vec_result.push_back(name_value);
		}
	}
	
	return 0; // return 0, to continue sql exec
}

int cb_sqlite_proc_select(void* cb_info, int argc, char **argv, char **azColName) {
	int i = 0;
	TAG_SQLITE3_CALLBACK_INFO* p_cb_info = (TAG_SQLITE3_CALLBACK_INFO*)cb_info;
	TAG_COL_NAME_AND_VALUE name_value;

	if (NULL != p_cb_info) {
		p_cb_info->clear();
		for(i = 0; i < argc; i++) {
			name_value.set(i, azColName[i], argv[i]);
			p_cb_info->vec_result.push_back(name_value);
		}
	}

	// i want to read only one row set
	return 1; // return not 0, to stop sql exec
}

bool fn_open_db(sqlite3*& db)
{
	int i_rc = 0;

	// Open database
	// sqlite3_open and sqlite3_open_v2 : db name is utf-8
	i_rc = sqlite3_open_v2(
		SQLITE_OBJ_DATABASE_PATH_NAME, // Database filename (UTF-8)
		&db, // OUT: SQLite db handle
		SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_SHAREDCACHE, // Flags
		NULL // Name of VFS module to use
		);

	// @note 
	// 一个程序读,一个程序写时,出现了如下错误
	// fn_sql_exec error = [database is locked]
	// 读的程序和写的程序如果在读失败和写失败后,继续尝试执行各自读和写的sql, 会有成功的一天
	// 但是这样就有问题了, 我写失败,难道我还要重试? 谁知道要尝试多少次才会成功?
	// 每执行一次SQL都有可能失败,这谁受的了:(
	// 由于多进程的读写,即使加上SQL执行失败的重试, 也会使sqlite数据库的访问变成串行的了. 渣...

	return (SQLITE_OK == i_rc);
}

void fn_close_db(sqlite3*& db)
{
	if (NULL != db) {
		sqlite3_close(db);
		db = NULL;
	}
}

bool fn_open_or_create_tbl(sqlite3* db, const char* psz_tbl_name, const char* psz_field_declare)
{
	int i_rc = SQLITE_ERROR;
	char sz_buf[MAX_SQL_LENGTH] = {'\0'};
	std::string str_err_msg = "";
	TAG_SQLITE3_CALLBACK_INFO cb_info;
	TAG_COL_NAME_AND_VALUE name_value;
	
	do {
		if ((NULL == db) 
			|| (NULL == psz_tbl_name) 
			|| (NULL == psz_field_declare))
		{
			break;
		}

		// check tbl is exist
		sprintf(sz_buf, 
			"select count(type) from sqlite_master where type='table' and name='%s'", 
			psz_tbl_name);
		
		if (!fn_sql_exec(db, sz_buf, str_err_msg, &cb_info, cb_sqlite_proc_insert)) {
			MYLOG_D("1 fn_sql_exec error = [%s]\n", str_err_msg.c_str());
			break;
		} 

		if (cb_info.vec_result.size() <= 0) {
			break;
		}

		name_value = cb_info.pop_first();
		if (name_value.str_value == "1") {
			// tbl is exist
			i_rc = SQLITE_OK;
			break;
		}

		// table not exist, create it
		sprintf(sz_buf, 
			"CREATE TABLE %s(%s);",
			psz_tbl_name,
			psz_field_declare);
		
		if (!fn_sql_exec(db, sz_buf, str_err_msg, &cb_info, cb_sqlite_proc_insert)) {
			MYLOG_D("2 fn_sql_exec error = [%s]\n", str_err_msg.c_str());
			break;
		}
		
		i_rc = SQLITE_OK;
		MYLOG_D("table created successfully\n");
	} while (0);

	return (SQLITE_OK == i_rc);
}

void fn_free_sqlite_msg(char*& psz_msg)
{
	if (NULL != psz_msg) {
		sqlite3_free(psz_msg);
		psz_msg = NULL;
	}
}

int fn_test_sqlite_read()
{
	sqlite3* db = NULL;
	char sz_buf[MAX_SQL_LENGTH] = {'\0'};
	int i_err_code = 0;
	std::string str_err_msg;
	TAG_SQLITE3_CALLBACK_INFO cb_info;
	TAG_COL_NAME_AND_VALUE name_value;

	do {
		if (!fn_open_db(db)) {
			MYLOG_D("can't open database: %s\n", sqlite3_errmsg(db));
			break;
		}

		MYLOG_D("opened database successfully\n");

		sprintf(sz_buf, "%s",
				"ID INT PRIMARY KEY NOT NULL," \
				"NAME TEXT NOT NULL," \
				"AGE INT NOT NULL," \
				"ADDRESS CHAR(50)," \
				"SALARY REAL");

		if (!fn_open_or_create_tbl(db, SQLITE_OBJ_TABLE_NAME, sz_buf)) {
			break;
		}

		do {
			if (is_file_exist(FLAG_FILE_TO_STOP_READ, i_err_code, str_err_msg)) {
				MYLOG_D("find stop flag file[%s], stop read now\n", FLAG_FILE_TO_STOP_READ);
				break;
			}

			sprintf(sz_buf, 
				"SELECT * from %s;",
				SQLITE_OBJ_TABLE_NAME);

			if (!fn_sql_exec(db, 
					sz_buf, 
					str_err_msg,
					&cb_info,
					cb_sqlite_proc_select))
			{
				MYLOG_D("3 fn_sql_exec error : %s\n", str_err_msg.c_str());
			} else {
				if (cb_info.vec_result.size() > 0) {
					cb_info.show_result();
					// process row set was read back
					// ...
					// delete it
					name_value = cb_info.pop_first();
					if (name_value.str_name == "ID") {
						sprintf(sz_buf, 
							"DELETE from %s where ID=%s;",
							SQLITE_OBJ_TABLE_NAME,
							name_value.str_value.c_str());

						if (!fn_sql_exec(db, 
							sz_buf, 
							str_err_msg,
							&cb_info,
							cb_sqlite_proc_select))
						{
							MYLOG_D("delete failed : %s\n", str_err_msg.c_str());
						} else {
							MYLOG_D("delete one row ok\n");
						}
					} else {
						MYLOG_D("can't find first col name is \"ID\"\n");
					}
				}else {
					MYLOG_D("can't read any row set\n");
				}
			}
		} while (1);
	} while (0);

	fn_close_db(db);

	return 0;
}

int fn_test_sqlite_write()
{
	sqlite3* db = NULL;
	char sz_buf[MAX_SQL_LENGTH] = {'\0'};
	int i_err_code = 0;
	std::string str_err_msg;
	int i_id = 0;
	TAG_SQLITE3_CALLBACK_INFO cb_info;

	do {
		if (!fn_open_db(db)) {
			MYLOG_D("can't open database: %s\n", sqlite3_errmsg(db));
			break;
		}

		MYLOG_D("opened database successfully\n");

		sprintf(sz_buf, "%s",
				"ID INT PRIMARY KEY NOT NULL," \
				"NAME TEXT NOT NULL," \
				"AGE INT NOT NULL," \
				"ADDRESS CHAR(50)," \
				"SALARY REAL");

		if (!fn_open_or_create_tbl(db, SQLITE_OBJ_TABLE_NAME, sz_buf)) {
			break;
		}

		do {
			if (is_file_exist(FLAG_FILE_TO_STOP_WRITE, i_err_code, str_err_msg)) {
				MYLOG_D("find stop flag file[%s], stop write now\n", FLAG_FILE_TO_STOP_WRITE);
				break;
			}

			sprintf(sz_buf, 
				"INSERT INTO %s (ID,NAME,AGE,ADDRESS,SALARY) VALUES (%d, 'Paul', 32, 'California', 20000.00 );",
				SQLITE_OBJ_TABLE_NAME,
				i_id++);

			if (!fn_sql_exec(db, 
					sz_buf, 
					str_err_msg,
					&cb_info,
					cb_sqlite_proc_insert))
			{
				MYLOG_D("4 fn_sql_exec error : %s\n", str_err_msg.c_str());
			}

			MYLOG_D("insert ok, id = %d\n", i_id - 1);
			cb_info.show_result();
		} while (1);
	} while (0);

	fn_close_db(db);

	return 0;
}

bool fn_sql_exec(
	sqlite3* db, 
	const char* psz_sql, 
	std::string& str_err_msg, 
	TAG_SQLITE3_CALLBACK_INFO* p_cb_info, 
	PFN_cb_sqlite_proc pfn_cb)
{
	int i_rc = SQLITE_ERROR;
	char sz_buf[MAX_SQL_LENGTH] = {'\0'};
	char* psz_err_msg = NULL;
	
	do {
		str_err_msg.clear();
		if ((NULL == db) || (NULL == psz_sql)) {
			break;
		}

		strcpy(sz_buf, psz_sql);
		i_rc = sqlite3_exec(db, sz_buf, pfn_cb, p_cb_info, &psz_err_msg);
		if ((SQLITE_OK != i_rc) && (SQLITE_ABORT != i_rc)) {
			str_err_msg = psz_err_msg;
			fn_free_sqlite_msg(psz_err_msg);
			break;
		}
	} while (0);

	return ((SQLITE_OK == i_rc) || (SQLITE_ABORT == i_rc));
}

int fn_sqlite_db_init()
{
	int i_rc = 0;
	
	MYLOG_D("sqlite3_libversion() = [%s]\n", sqlite3_libversion());
	MYLOG_D("sqlite3_libversion_number() = [%d]\n", sqlite3_libversion_number());
	MYLOG_D("sqlite3_sourceid() = [%s]\n", sqlite3_sourceid());

	
	sqlite3_shutdown();
	sqlite3_config(SQLITE_CONFIG_MULTITHREAD);

	i_rc = sqlite3_threadsafe();
	// SQLITE_THREADSAFE = 1 or 2, but not 0(0 is not thread safe)
	// 0 is 单线程模式
	// 1 is 串行模式
	// 2 is 多线程模式
	MYLOG_D("sqlite3_threadsafe() = %d\n", i_rc);
	if (i_rc > 0) {
		i_rc = sqlite3_config(SQLITE_CONFIG_SERIALIZED);
		MYLOG_D("sqlite3_config() = %d\n", i_rc);
		if (SQLITE_OK == i_rc) {
			MYLOG_D("can now use sqlite on multiple threads, using the same connection\n");
		} else {
			MYLOG_D("!! warning : setting sqlite thread safe mode to serialized failed\n");
		}
	} else {
		MYLOG_D("!! warning : current sqlite database is not compiled to be threadsafe\n");
	}

	sqlite3_initialize();

	// 当多进程一起访问同一个数据库文件时
	// 都在此点开始运行, 也会有如下错误
	// database is locked
	
	MYLOG_D("press enter key to continue\n");
	getchar();

	return 0;
}

bool is_file_exist(const char* psz_file_path_name, int& i_err_code, std::string& str_err_msg)
{
    bool b_rc = false;
    int i_rc = -1;

    do {
        if ((NULL == psz_file_path_name)
            || (0 == strlen(psz_file_path_name))) {
            i_err_code = -1;
            str_err_msg = "param 1 error";
            break;
        }

        i_rc = access(psz_file_path_name, F_OK);

        if (0 != i_rc) {
            i_err_code = errno;
            str_err_msg = strerror(i_err_code);
        } else {
            b_rc = true;
        }
    } while (0);

    return b_rc;
}


// @file const_define.h

#if not defined(__CONST_DEFINE_H__)
#define __CONST_DEFINE_H__

#include <string.h>
#include <string>
#include <list>

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) \
    if (NULL != (p)) { \
        delete (p); \
        (p) = NULL; \
    }
#endif // #ifndef SAFE_DELETE

#ifndef SAFE_DELETE_ARY
#define SAFE_DELETE_ARY(p) \
    if (NULL != (p)) { \
        delete[] (p); \
        (p) = NULL; \
    }
#endif // #ifndef SAFE_DELETE

#define TITLE_LINE80 "================================================================================"
#define LINE80 "--------------------------------------------------------------------------------"

#if not defined(MYLOG_D)
#define MYLOG_D printf
#endif // #if not defined(MYLOG_D)

#define SQLITE_OBJ_DATABASE_PATH_NAME "/var/log/my_sqlite_test_2.db"
#define SQLITE_OBJ_TABLE_NAME "tbl_test_3"

#define FLAG_FILE_TO_STOP_READ "/var/log/flag_stop_read"
#define FLAG_FILE_TO_STOP_WRITE "/var/log/flag_stop_write"

#define MAX_SQL_LENGTH (1024 * 8)
#define MAX_MSG_LENGTH (1024 * 4)

typedef struct _tag_col_name_and_value {
	int i_index; 
	std::string str_name;
	std::string str_value;

	_tag_col_name_and_value()
	{
		clear();
	}

	void clear()
	{
		i_index = 0;
			
		{
			std::string str;
			str_name.swap(str);
		}

		{
			std::string str;
			str_value.swap(str);
		}
	}

	void set(int i_index, const char* name, const char* value)
	{
		clear();

		this->i_index = i_index;
		str_name = ((NULL != name) ? name : "");
		str_value = ((NULL != value) ? value : "");
	}
}TAG_COL_NAME_AND_VALUE;

typedef struct _tag_sqlite3_callback_info {
	// 出参
	std::list<TAG_COL_NAME_AND_VALUE> vec_result;
	std::list<TAG_COL_NAME_AND_VALUE>::iterator it;

	_tag_sqlite3_callback_info()
	{
		clear();	
	}

	void clear()
	{
		vec_result.clear();
	}

	TAG_COL_NAME_AND_VALUE pop_first()
	{
		TAG_COL_NAME_AND_VALUE rc;

		for (it = vec_result.begin(); it != vec_result.end(); it++) {
			rc = *it;
			vec_result.pop_front();
			break;
		}

		return rc;
	}

	void show_result() {
		TAG_COL_NAME_AND_VALUE rc;

		MYLOG_D("%s\n", TITLE_LINE80);
		MYLOG_D("%s\n", "show_result");
		MYLOG_D("%s\n", LINE80);
		for (it = vec_result.begin(); it != vec_result.end(); it++) {
			rc = *it;
			MYLOG_D("[%d] name = [%s], value = [%s]\n",
				rc.i_index,
				rc.str_name.c_str(),
				rc.str_value.c_str());
		}
	}
}TAG_SQLITE3_CALLBACK_INFO;

#endif // #if not defined(__CONST_DEFINE_H__)

# ==============================================================================
# @file makefile
# ==============================================================================
# @note 
# howto build project
# 		make BIN_NAME="bin_name_by_you_want" rebuild
# makefile code need tab key not sapce key

MY_MAKE_FILE_PATH_NAME = $(MAKEFILE_LIST)

# macro from Makefile command line
# BIN_NAME

# macro to C project
MAKE_FILE_MACRO__BIN_NAME="make_file_macro__bin_name"

# var define on Makefile
BIN = output_not_give_bin_name
IS_BUILD_TYPE_VALID = 0

ifdef BIN_NAME
	IS_BUILD_TYPE_VALID = 1
	BIN = $(BIN_NAME)
	MAKE_FILE_MACRO__BIN_NAME=$(BIN_NAME)
else
	IS_BUILD_TYPE_VALID = 0
endif

LINE80 = --------------------------------------------------------------------------------

# CC = g++ -std=c++98
CC = g++

# -Werror is "warning as error"
CFLAGS = -Wall -Werror -g

INC = -I. -I../../doc/sqlite3_was_install/include/
LIBPATH = -L/usr/lib/ -L/usr/local/lib/ -L../../doc/sqlite3_was_install/lib/

ifeq (1, $(IS_BUILD_TYPE_VALID))
	LIBS = -lstdc++ -pthread -lsqlite3
else
	LIBS =
endif

DEPEND_CODE_DIR = ../common/ \

DEPEND_CODE_SRC = $(shell find $(DEPEND_CODE_DIR) -name '*.cpp')
DEPEND_CODE_OBJ = $(DEPEND_CODE_SRC:.cpp=.o)

ROOT_CODE_SRC = $(shell find ./ -name '*.cpp')
ROOT_CODE_OBJ = $(ROOT_CODE_SRC:.cpp=.o)

SUB_CODE_DIR = ./empty_dir
SUB_CODE_SRC = $(shell find $(SUB_CODE_DIR) -name '*.cpp')
SUB_CODE_OBJ = $(SUB_CODE_SRC:.cpp=.o)

.PHONY: help
help:
	clear
	@echo "usage:"
	@echo
	@echo "build project by given bin name"
	@echo "make BIN_NAME=\"bin_name_by_you_want\" rebuild"
	@echo

.PHONY: clean
clean:
	clear

	@echo
	@echo
	@echo
	@echo
	@echo
	@echo
	@echo
	@echo
	@echo
	@echo

	@echo
	@echo
	@echo
	@echo
	@echo
	@echo
	@echo
	@echo
	@echo
	@echo

	@echo
	@echo
	@echo
	@echo
	@echo

	@echo "make clean begin"
	@echo $(LINE80)

	@echo "@file $(MY_MAKE_FILE_PATH_NAME)"
	@echo "IS_BUILD_TYPE_VALID = $(IS_BUILD_TYPE_VALID)"
	@echo "BIN = $(BIN)"

	@echo $(LINE80)

	rm -f $(ROOT_CODE_OBJ) $(DEPEND_CODE_OBJ) $(SUB_CODE_OBJ)

ifeq (1, $(IS_BUILD_TYPE_VALID))
	rm -f ./$(BIN)
endif

	@echo "make clean over"

.PHONY: all
all:$(BIN)
	@echo $(LINE80)
	@echo make all
	chmod 777 ./$(BIN)
	find . -name "$(BIN)"

$(BIN) : $(ROOT_CODE_OBJ) $(DEPEND_CODE_OBJ) $(SUB_CODE_OBJ)
	$(CC) $(CFLAGS) -o $@ $^ $(SHLIBS) $(INC) $(LIBPATH) $(LIBS)

.cpp.o:
	$(CC) -c $(CFLAGS) -DMAKE_FILE_MACRO__BIN_NAME="\"$(MAKE_FILE_MACRO__BIN_NAME)\"" $^ -o $@ $(INC) $(LIBPATH) $(LIBS)

.PHONY: rebuild
rebuild:
	make -f $(MY_MAKE_FILE_PATH_NAME) clean

ifeq (1, $(IS_BUILD_TYPE_VALID))
	@echo $(LINE80)
	make -f $(MY_MAKE_FILE_PATH_NAME) all
	chmod 775 ./$(BIN)
	ldd ./$(BIN)
else
	@echo $(LINE80)
	@echo "error : Makefile command line input error, please see help"	
	@echo "please run => make help"	
	@echo $(LINE80)
endif


#!/bin/bash
# ==============================================================================
# @file build_all_project.sh
# ==============================================================================

make BIN_NAME="test_sqlite_thread_safe" rebuild


猜你喜欢

转载自blog.csdn.net/LostSpeed/article/details/83452393