2018年12月3日 星期一

有關 CMakeLists.txt 基本用法

編寫 Makefile 還要考慮到檔案相關性, 何不讓程式自動寫 Makefile 呢? cmake 就是做這種功能的, 簡單的像是編譯 tutor.cpp:
        g++   tutor.cpp   -lGLESv2   -lglfw    -lGLEW   -o   tutor
只要在根目錄用 cmake 的語法編輯檔案 CMakeLists.txt .  先替執行檔取名(例如 tutor), 把要編譯的程式檔加入 add_exetuable(...), 再把需要聯結的程式庫加進 target_link_libraries(...), 例如:
      add_executable(tutor   tutor.cpp)
      target_link_libraries(tutor GLESv2 glfw GLEW)
兩行就能搞定, 通常在專案的根目錄建個子目錄 build, 讓編譯的暫存檔放在裏面, 接著進入子目錄下命令 cmake .. 完成後自動生成 Makefile, 之後跟平常一樣, 只要進入子目錄 build 裏面,輸入 make 就可以編譯程式, 例如:
      [-d build ] || mkdir build
      cd build && cmake ..
      make
移除檔案時只要將專案的目錄 build 整個刪除就清乾淨了. 簡單寫個 Makefile 來支援 cmake 測試一下:
#Makefile to generate CMakeLists.txt
userpath= $(shell pwd)
build   = $(userpath)/build
target  = $(build)/tutor
run: $(target)
    $<

$(target): $(build) tutor.cpp
    cd $<; make
$(build): CMakeLists.txt
    @[ -d $@ ] || mkdir $@
    @[ -f $< ] &&    cd $@ && cmake .. || echo missing $<;echo
CMakeLists.txt: 
    echo "add_executable(tutor tutor.cpp)" > $@
    echo "target_link_libraries(tutor GLESv2 glfw GLEW)" >> $@
glfwlib:
    sudo apt-get install  libglfw3-dev  libgles2-mesa-dev  libglew-dev
tutor: tutor.cpp
    g++ $< -lGLESv2 -lglfw -lGLEW -o $@ && ./$@
clean:
    rm -rf build
     rm tutor
    clear

簡單的 c++ 畫圖程式碼:
// tutor.cpp
#include "stdio.h"
#include "GL/glew.h"
#include "GLFW/glfw3.h"
int    error(int errno, const char *msg){
    glfwTerminate();
    printf("Errir %d:%s",errno, msg);
    return -1;
 }
class  myShader  {  
    private:  
        struct glbuffer   { GLuint bind; int size; };//  dataclass contain 2 members  
        class  methodbind { // buffer inject myShader's method
            private:
                myShader *thispointer; // link with outer class
            public:
                glbuffer  buffer;
                methodbind(myShader  *pointer)       { thispointer = pointer; buffer.size=0; }
                void vec3(const char *name)                 { glBindBuffer(GL_ARRAY_BUFFER, buffer.bind); thispointer -> attribute(name, 3); }
         };
        int  programid, shaderid, bufferno;
        void attribute(const char *name, int size)   {
            int location = glGetAttribLocation(programid, name);
            glEnableVertexAttribArray(location);
            glVertexAttribPointer(location, size, GL_FLOAT, GL_FALSE, 0, 0);
         }      
        void detachshader(int sid)                   { glDetachShader(programid, sid); glDeleteShader(sid) ; }
        void assemble(int &&sid, const char **string){
            glShaderSource (sid, 1, string , NULL);
            glCompileShader(sid); 
            glAttachShader(programid, sid);
            if( shaderid == 0 ) shaderid = sid; // push shader id
            else {    // accumulate upto 2 shaders, ready to link
                glLinkProgram(programid);    // link with the program id
                detachshader(sid);    // detach current sid
                detachshader(shaderid);// pop previous sid and detach
                shaderid=0;            // restart to accumulate sid
            }       
         }  
        #define buffernMAX 100
        glbuffer  shaderbuffer[buffernMAX];      
        glbuffer& addbuffer(GLuint bid, int size)    {
            shaderbuffer[bufferno].bind = bid;
            shaderbuffer[bufferno].size = size;
            return shaderbuffer[bufferno++];              
         }
        auto arraydata(GLfloat *array , int size)    { 
            GLuint bid;
            glGenBuffers(1, &bid);
            glBindBuffer(GL_ARRAY_BUFFER, bid);
            glBufferData(GL_ARRAY_BUFFER, size, array, GL_STATIC_DRAW);
            return addbuffer(bid, size);
         }
    public:  // public area  
        methodbind  point = methodbind(this);// bind with this
        GLFWwindow* context;
        methodbind& pointbuffer(GLfloat *array, int size) {
            if( bufferno < buffernMAX && size > 0 ) point.buffer = arraydata(array, size);
            return point;
         }
        myShader(int width, int height,const char *vcode, const char *fcode)    {
            bufferno = 0;
            shaderid = 0;
            if( glfwInit() )  {
                glfwWindowHint(GLFW_SAMPLES, 4);
                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
                context = glfwCreateWindow(width, height, "myShader", NULL, NULL);
                if( context ) {
                    glfwMakeContextCurrent(context);
                    if ( glewInit() == GLEW_OK ) {
                        glfwSetInputMode(context, GLFW_STICKY_KEYS, GL_TRUE);
                        glClearColor(0.0, 0.0, 0.0, 0.0);                     
                        programid = glCreateProgram();
                        assemble(glCreateShader(GL_VERTEX_SHADER)  , &vcode);
                        assemble(glCreateShader(GL_FRAGMENT_SHADER), &fcode);
                    }
                }
             }
         }
        void close() {
            for (int i = 0; i < bufferno; i ++)  glDeleteBuffers(1, &shaderbuffer[i].bind);
            bufferno = 0;
            glDeleteProgram(programid);
            glfwTerminate();
         }
        bool draw()  {
            glClear( GL_COLOR_BUFFER_BIT );
            glUseProgram(programid);
            glDrawArrays(GL_TRIANGLES, 0, point.buffer.size); // draw points
            glfwSwapBuffers(context);
            glfwPollEvents();
            if( glfwGetKey(context, GLFW_KEY_ESCAPE ) == GLFW_PRESS ) return false;
            return ! glfwWindowShouldClose(context);
         }
 };

int main( void ) {
    const char *vcode = R"EOF(// Vertex shader program:
        attribute vec3 position;
        void main() {
            gl_Position = vec4(position, 1.0);
        }
    )EOF";
    const char *fcode= R"EOF(// Fragment shader program:
        void main() {
            gl_FragColor = vec4(1,0,0,1);
        }
    )EOF";  
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f,
    };  
    auto a =  myShader(800, 600, vcode, fcode);
    if( ! a.context )  return error(0, "initGL Error");  
    a.pointbuffer(vertices,sizeof(vertices)).vec3("position");// begin to transfer vertices
    while( a.draw() );
    a.close();
    return 0;
 }