Coverity全量扫描Jenkins 流程模板

使用下列的Coverity 模板,可完成在 Jenkins 上的Coverity 全量扫描、上传全量报告到服务器端

模板使用办法
修改模板Jenkinsfile

  • 拷贝所需要类型的模板,例如java使用:coverity_java
  • 根据项目实际情况修改Jenkinsfile中 docker image、编译命令、代码仓库分支等
  • 将修改完的Coverity Jenkins file ,提交到项目代码仓库的scm/project/目录下
  • 参考文档创建Jenkins job:创建Jenkins job
  • 配置Jenkins job ,调用代码仓库中的Jenkinsfile Jenkins job调用代码仓库中的Jenkinsfile
    模板详解
    以下为四种类型的Coverity扫描模板详解

Python 扫描

pipeline {
    agent { //2~32 初始化环境,images需要换成符合项目编译要求的镜像
        kubernetes {
            label "${UUID.randomUUID().toString()}"
            yaml """
                metadata:
                  labels:
                    some-label: some-label-value
                    class: KubernetesDeclarativeAgentTest
                  namespace: scm
                spec:
                  nodeSelector:
                    jenkins-ci: "true"
                  containers:
                    - name: prepare
                      image: hub.hobot.cc/builder/cicd-common-tool:v10
                      command:
                      - cat
                      tty: true
                      env:
                        - name: CONTAINER_ENV_VAR
                          value: prepare
                      volumeMounts:
                        - name: build-cache
                          mountPath: /home/ci-cache
                  volumes:
                    - name: build-cache
                      hostPath:
                        path:  /gpfs-test/cicd
            """
        }
    }
    options {
      gitLabConnection('Gitlab connector by hobot.ci')
    }
    parameters { //参数,默认仓库、分支 需要修改
        string defaultValue: 'git@gitlab.hobot.cc:XXX/XXX.git', description: 'Enter gitlabSourceRepoSshUrl', name: 'gitlabSourceRepoSshUrl', trim: true
        string defaultValue: 'master', description: 'Enter BRANCH_NAME', name: 'BRANCH_NAME', trim: true
        booleanParam defaultValue: true, description: '如果需要进行coverity扫描,请勾选', name: 'COVERITY_SACAN'
        booleanParam defaultValue: true, description: '如果需要进行coverity扫描报告上传,请勾选', name: 'COVERITY_UPLOAD'
    }
    environment {
        LANG = 'en_US.UTF-8'
    }
    triggers { //定时,每天五点触发
        cron 'H 5 * * *'
    }
    stages{
        stage('prepare') {
            steps {
                container('prepare') {
                    script {
                        sh 'env'
                        println Jenkins.instance.getNode("${env.NODE_NAME}").toComputer().getLog()
                        sh 'ls ./'
                        git branch: 'master', credentialsId: '', url: 'git@gitlab.hobot.cc:XXX/XXX.git'
                    }
                }
            }
        }
        stage('coverity') {
            when {
                environment name: 'COVERITY_SACAN', value: 'true'
                beforeAgent true
            }
            steps {
                container('prepare') {
                    script {
                        coverity() //执行coverity()函数
                    }
                }
                container('prepare') {
                    script {
                        coverity_prepare() //执行coverity_prepare()函数
                        archiveArtifacts 'FUNCTION.metrics.xml,summary.txt'
                        publishHTML([allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: 'coverity_html', reportFiles: 'index.html', reportName: 'CoverityReport', reportTitles: '']) //上传报告到Jenkins 页面
                    }
                }
            }
        }
    }
}

def coverity(){ // 77~123 coverity扫描,无特殊需求不需要修改
    dir("/home/jenkins/workspace/coverity") {
        sh 'pwd'
        sh 'cp -a ${WORKSPACE}/. .'
        sh '''#!/bin/bash -ex
            coverity_tool_path='/home/ci-cache/synopsys/coverity/tools/'
            coverity_cache_path='/home/ci-cache/synopsys/coverity/build_cache/'
            coverity_auth_key_path='/home/ci-cache/synopsys/coverity/tools/upload.key'
            repo_path=`echo ${gitlabSourceRepoSshUrl} | sed 's/.git//g' | sed 's/gitlab.hobot.cc://g'`
            echo ${repo_path}
            stream_path=`echo ${repo_path} | sed 's/\\//|/g'`
            stream_path=${stream_path}\\|${BRANCH_NAME}
            echo ${stream_path}
            build_cache_path='/home/jenkins/workspace/coverity_cache'
            echo ${build_cache_path}
            
            export PATH=${coverity_tool_path}cov-analysis-linux64-2021.12.0/bin:/home/softwares/gcc-linaro-6.5.0-2018.12-x86_64_aarch64-linux-gnu/bin:$PATH
            
            export COVERITY_UNSUPPORTED_COMPILER_INVOCATION=1
            
            cov-configure --python 
            cov-capture --dir ${build_cache_path} --source-dir ./
            cov-manage-emit --dir ${build_cache_path} add-other-hosts
            cov-analyze --dir ${build_cache_path} --all -force
            
            cov-format-errors --dir ${build_cache_path} --html-output /home/jenkins/workspace/coverity/coverity_html
            if [ ${COVERITY_UPLOAD} == 'true' ];then
                cov-commit-defects --dir ${build_cache_path} --url http://coverity-earth.hobot.cc:8080 --auth-key-file ${coverity_auth_key_path} --stream ${stream_path}
            fi
        '''
    }
}

def coverity_prepare(){
    sh '''
        coverity_tool_path='/home/ci-cache/synopsys/coverity/tools/'
        coverity_cache_path='/home/ci-cache/synopsys/coverity/build_cache/'
        repo_path=`echo ${gitlabSourceRepoSshUrl} | sed 's/.git//g' | sed 's/gitlab.hobot.cc://g'`
        build_cache_path='/home/jenkins/workspace/coverity_cache'                          
        cp ${build_cache_path}/output/FUNCTION.metrics.xml.gz .
        gunzip FUNCTION.metrics.xml.gz
        python2 ${coverity_tool_path}parse_mcc.py FUNCTION.metrics.xml 5
        
        cp ${build_cache_path}/output/summary.txt .
        cp -r /home/jenkins/workspace/coverity/coverity_html .
    '''
}

Go 扫描

pipeline {
    agent { //2~52 初始化编译环境,images需要换成符合项目编译要求的镜像
        kubernetes {
            label "${UUID.randomUUID().toString()}"
            yaml """
                metadata:
                  labels:
                    some-label: some-label-value
                    class: KubernetesDeclarativeAgentTest
                  namespace: scm
                spec:
                  nodeSelector:
                    jenkins-ci: "true"
                  containers:
                    - name: jnlp
                      image: hub.hobot.cc/ci/jnlp-slave:3.29-1-alpine
                      resources:
                        requests:
                          cpu: 1000m
                          memory: 2048Mi
                        limits:
                          cpu: 2000m
                          memory: 4096Mi
                    - name: prepare
                      image: hub.hobot.cc/builder/cicd-common-tool:v8
                      command:
                      - cat
                      tty: true
                      env:
                        - name: CONTAINER_ENV_VAR
                          value: prepare
                      volumeMounts:
                        - name: build-cache
                          mountPath: /home/ci-cache
                    - name: build
                      image: hub.hobot.cc/builder/data_pipeline_docker:v3
                      command:
                      - cat
                      tty: true
                      env:
                        - name: CONTAINER_ENV_VAR
                          value: build
                      volumeMounts:
                        - name: build-cache
                          mountPath: /home/ci-cache
                  volumes:
                    - name: build-cache
                      hostPath:
                        path:  /gpfs-test/cicd
            """
        }
    }
    parameters {//编译参数,默认仓库、分支 需要修改
        string defaultValue: 'git@gitlab.hobot.cc:/xxx.git', description: 'Enter PH REVISION ID', name: 'gitlabSourceRepoSshUrl', trim: true
        string defaultValue: 'develop', description: '', name: 'BRANCH_NAME', trim: true
        booleanParam defaultValue: true, description: '', name: 'COVERITY_UPLOAD'
    }
    environment {
        LANG = 'en_US.UTF-8'
    }
    stages {
        stage('prepare') {
            environment {
                SSH_KEY_FILE = credentials('725a97cb-ba0c-45ad-a59f-f366e217a017')
            }
            steps {
                container('prepare') {
                    script {
                        println Jenkins.instance.getNode("${env.NODE_NAME}").toComputer().getLog()
                        git branch: 'develop', credentialsId: '', url: 'git@gitlab.hobot.cc:/xxx.git'
                    }
                }
            }
        }
        stage('coverity') {
            parallel {
                stage('coverity') {
                    steps {
                        container('build') {
                            script {
                                if(env.gitlabSourceRepoSshUrl && env.BRANCH_NAME){
                                    coverity() // 调用coverity 函数
                                }
                            }
                        }
                        container('prepare') {
                            script {
                                if(env.gitlabSourceRepoSshUrl && env.BRANCH_NAME){
                                    sh 'echo coverity' 
                                    coverity_prepare() 
                                    archiveArtifacts 'FUNCTION.metrics.xml,summary.txt'
                                    publishHTML([allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: 'coverity_html', reportFiles: 'index.html', reportName: 'CoverityReport', reportTitles: '']) //将coverity报告上传到Jenkins页面
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    options {
        gitLabConnection('Gitlab connector by hobot.ci')
    }
    triggers { //设置定时每天五点跑
        cron 'H 5 * * *'
    }
}


def coverity(){ //130行需要修改为自己的编译命令
    dir("/home/jenkins/workspace/coverity") {
        sh 'pwd'
        sh 'cp -a ${WORKSPACE}/. .'
        sh '''#!/bin/bash -ex
            coverity_tool_path='/home/ci-cache/synopsys/coverity/tools/'
            coverity_auth_key_path='/home/ci-cache/synopsys/coverity/tools/upload.key'
            repo_path=`echo ${gitlabSourceRepoSshUrl} | sed 's/.git//g' | sed 's/gitlab.hobot.cc://g'`
            echo ${repo_path}
            stream_path=`echo ${repo_path} | sed 's/\\//|/g'`
            stream_path=${stream_path}\\|${BRANCH_NAME}
            echo ${stream_path}
            build_cache_path='/home/jenkins/workspace/coverity_cache'
            echo ${build_cache_path}
            
            export PATH=${coverity_tool_path}cov-analysis-linux64-2021.12.0/bin:/home/softwares/gcc-linaro-6.5.0-2018.12-x86_64_aarch64-linux-gnu/bin:$PATH
            
            export COVERITY_UNSUPPORTED_COMPILER_INVOCATION=1
            
            cov-configure --go
            cov-build --dir ${build_cache_path} sh build/dev/apiserver/build.sh
            cov-manage-emit --dir ${build_cache_path} add-other-hosts
            cov-analyze --dir ${build_cache_path} -force
            
            cov-format-errors --dir ${build_cache_path} --html-output /home/jenkins/workspace/coverity/coverity_html
            if [ ${COVERITY_UPLOAD} == 'true' ];then
                cov-commit-defects --dir ${build_cache_path} --url http://coverity-earth.hobot.cc:8080 --auth-key-file ${coverity_auth_key_path} --stream ${stream_path}
            fi
        '''
    }
}

def coverity_prepare(){
    sh '''
        coverity_tool_path='/home/ci-cache/synopsys/coverity/tools/'
        repo_path=`echo ${gitlabSourceRepoSshUrl} | sed 's/.git//g' | sed 's/gitlab.hobot.cc://g'`
        build_cache_path='/home/jenkins/workspace/coverity_cache'                              
        cp ${build_cache_path}/output/FUNCTION.metrics.xml.gz .
        gunzip FUNCTION.metrics.xml.gz
        python2 ${coverity_tool_path}parse_mcc.py FUNCTION.metrics.xml 5
        
        cp ${build_cache_path}/output/summary.txt .
        cp -r /home/jenkins/workspace/coverity/coverity_html .
    '''
}

Java 扫描

pipeline {
    agent { //2~52 初始化环境 images需要换成符合项目编译要求的镜像
        kubernetes {
            label "${UUID.randomUUID().toString()}"
            yaml """
                metadata:
                  labels:
                    some-label: some-label-value
                    class: KubernetesDeclarativeAgentTest
                  namespace: scm
                spec:
                  nodeSelector:
                    jenkins-ci: "true"
                  containers:
                    - name: jnlp
                      image: hub.hobot.cc/ci/jnlp-slave:3.29-1-alpine
                      resources:
                        requests:
                          cpu: 1000m
                          memory: 2048Mi
                        limits:
                          cpu: 2000m
                          memory: 4096Mi
                    - name: prepare
                      image: hub.hobot.cc/builder/cicd-common-tool:v8
                      command:
                      - cat
                      tty: true
                      env:
                        - name: CONTAINER_ENV_VAR
                          value: prepare
                      volumeMounts:
                        - name: build-cache
                          mountPath: /home/ci-cache
                    - name: build
                      image: hub.hobot.cc/builder/aidi_issue_qa:v1_coverity
                      command:
                      - cat
                      tty: true
                      env:
                        - name: CONTAINER_ENV_VAR
                          value: build
                      volumeMounts:
                        - name: build-cache
                          mountPath: /home/ci-cache
                  volumes:
                    - name: build-cache
                      hostPath:
                        path:  /gpfs-test/cicd
            """
        }
    }
    parameters { //编译参数,默认仓库、分支 需要修改
        string defaultValue: 'git@gitlab.hobot.cc:/xxx/xxx.git', description: 'Enter PH REVISION ID', name: 'gitlabSourceRepoSshUrl', trim: true
        string defaultValue: 'master', description: '', name: 'BRANCH_NAME', trim: true
        booleanParam defaultValue: true, description: '', name: 'COVERITY_UPLOAD'
    }
    environment {
        LANG = 'en_US.UTF-8'
    }
    stages{
        stage('prepare') {
            environment {
                LANG = 'en_US.UTF-8'
                SSH_KEY_FILE = credentials('')
            }
            steps {
                container('prepare') {
                    script {
                        println Jenkins.instance.getNode("${env.NODE_NAME}").toComputer().getLog()
                        git branch: 'master', credentialsId: '', url: 'git@gitlab.hobot.cc:/xxx/xxx.git'
                    }
                }
            }
        }
        stage('build') {
            steps {
                container('prepare') {
                    script {
                        if(env.gitlabSourceRepoSshUrl && env.BRANCH_NAME){
                            coverity()
                        }
                    }
                }
                container('prepare') {
                    script {
                        if(env.gitlabSourceRepoSshUrl && env.BRANCH_NAME){
                            sh 'echo coverity'
                            coverity_prepare()
                            archiveArtifacts 'FUNCTION.metrics.xml,summary.txt'
                            publishHTML([allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: 'coverity_html', reportFiles: 'index.html', reportName: 'CoverityReport', reportTitles: '']) //将报告上传到Jenkins页面
                        }
                    }
                }
            }
        }
    }
    options {
      gitLabConnection('Gitlab connector by hobot.ci')
    }
    triggers { //设置定时,每天三点编译
        cron 'H 3 * * *'
    }
}

def coverity(){
    dir("/home/jenkins/workspace/coverity") {
        sh 'pwd'
        sh 'cp -a ${WORKSPACE}/. .'
        sh '''#!/bin/sh -ex
            coverity_tool_path='/home/ci-cache/synopsys/coverity/tools/'
            coverity_auth_key_path='/home/ci-cache/synopsys/coverity/tools/upload.key'
            repo_path=`echo ${gitlabSourceRepoSshUrl} | sed 's/.git//g' | sed 's/gitlab.hobot.cc://g'`
            echo ${repo_path}
            stream_path=`echo ${repo_path} | sed 's/\\//|/g'`
            stream_path=${stream_path}\\|${BRANCH_NAME}
            echo ${stream_path}
            build_cache_path='/home/jenkins/workspace/coverity_cache'
            echo ${build_cache_path}
            
            export PATH=${coverity_tool_path}cov-analysis-linux64-2021.12.0/bin:/home/softwares/gcc-linaro-6.5.0-2018.12-x86_64_aarch64-linux-gnu/bin:$PATH
            
            export COVERITY_UNSUPPORTED_COMPILER_INVOCATION=1
            cov-configure --java

            cov-capture --project-dir ./ --dir ${build_cache_path}
            cov-manage-emit --dir ${build_cache_path} add-other-hosts
            cov-analyze --dir ${build_cache_path} --webapp-security --all --disable-fb --tu-pattern "file('/home/jenkins/workspace/coverity/')"
            
            cov-format-errors --dir ${build_cache_path} --html-output /home/jenkins/workspace/coverity/coverity_html
            if [ ${COVERITY_UPLOAD} == 'true' ];then
                cov-commit-defects --dir ${build_cache_path} --url http://coverity-earth.hobot.cc:8080 --auth-key-file ${coverity_auth_key_path} --stream ${stream_path}
            fi
        '''
    }
}

def coverity_prepare(){
    sh '''
        coverity_tool_path='/home/ci-cache/synopsys/coverity/tools/'
        repo_path=`echo ${gitlabSourceRepoSshUrl} | sed 's/.git//g' | sed 's/gitlab.hobot.cc://g'`
        build_cache_path='/home/jenkins/workspace/coverity_cache'                               
        cp ${build_cache_path}/output/FUNCTION.metrics.xml.gz .
        gunzip FUNCTION.metrics.xml.gz
        python2 ${coverity_tool_path}parse_mcc.py FUNCTION.metrics.xml 5
        
        cp ${build_cache_path}/output/summary.txt .
        cp -r /home/jenkins/workspace/coverity/coverity_html .
    '''
}

c/c++ 扫描

pipeline { 
    agent { //2~42 初始化环境,images需要换成符合项目编译要求的镜像
        kubernetes {
            label "${UUID.randomUUID().toString()}"
            yaml """
                metadata:
                  labels:
                    some-label: some-label-value
                    class: KubernetesDeclarativeAgentTest
                  namespace: scm
                spec:
                  nodeSelector:
                    jenkins-ci: "true"
                  containers:
                    - name: prepare
                      image: hub.hobot.cc/builder/cicd-common-tool:v9
                      command:
                      - cat
                      tty: true
                      env:
                        - name: CONTAINER_ENV_VAR
                          value: prepare
                      volumeMounts:
                        - name: build-cache
                          mountPath: /home/ci-cache
                    - name: build
                      image: hub.hobot.cc/builder/halo:v1
                      command:
                      - cat
                      tty: true
                      env:
                        - name: CONTAINER_ENV_VAR
                          value: build
                      volumeMounts:
                        - name: build-cache
                          mountPath: /home/ci-cache
                  volumes:
                    - name: build-cache
                      hostPath:
                        path:  /gpfs-test/cicd
            """
        }
    }
    parameters { //编译参数,默认仓库、分支 需要修改
        string defaultValue: 'git@gitlab.hobot.cc:/xxx.git', description: 'Enter PH REVISION ID', name: 'gitlabSourceRepoSshUrl', trim: true
        string defaultValue: 'develop', description: '', name: 'BRANCH_NAME', trim: true
        booleanParam defaultValue: true, description: '', name: 'COVERITY_UPLOAD'
    }
    environment {
        LANG = 'en_US.UTF-8'
    }
    stages {
        stage('prepare') {
            steps {
                container('prepare') {
                    script {
                        println Jenkins.instance.getNode("${env.NODE_NAME}").toComputer().getLog()
                        git branch: 'develop', credentialsId: '', url: 'git@gitlab.hobot.cc:/xxx.git'
                    }
                }
            }
        }
        stage('coverity') {
            parallel {
                stage('coverity') {
                    steps {
                        container('build') {
                            script {
                                if(env.gitlabSourceRepoSshUrl && env.BRANCH_NAME){
                                    coverity()
                                }
                            }
                        }
                        container('prepare') {
                            script {
                                if(env.gitlabSourceRepoSshUrl && env.BRANCH_NAME){
                                    sh 'echo coverity'
                                    coverity_prepare()
                                    archiveArtifacts 'FUNCTION.metrics.xml,summary.txt'
                                    publishHTML([allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: 'coverity_html', reportFiles: 'index.html', reportName: 'CoverityReport', reportTitles: '']) //上传coverity报告到Jenkins页面
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    options {
        gitLabConnection('Gitlab connector by hobot.ci')
    }
}

def coverity(){
    dir("/home/jenkins/workspace/coverity") {
        sh 'pwd'
        sh 'cp -a ${WORKSPACE}/. .'
        sh '''#!/bin/bash -ex
            coverity_tool_path='/home/ci-cache/synopsys/coverity/tools/'
            coverity_cache_path='/home/ci-cache/synopsys/coverity/build_cache/'
            coverity_auth_key_path='/home/ci-cache/synopsys/coverity/tools/upload.key'
            repo_path=`echo ${gitlabSourceRepoSshUrl} | sed 's/.git//g' | sed 's/gitlab.hobot.cc://g'`
            echo ${repo_path}
            stream_path=`echo ${repo_path} | sed 's/\\//|/g'`
            stream_path=${stream_path}\\|${BRANCH_NAME}
            echo ${stream_path}
            build_cache_path='/home/jenkins/workspace/coverity_cache'
            echo ${build_cache_path}
            
            export PATH=${coverity_tool_path}cov-analysis-linux64-2021.12.0/bin:/home/softwares/gcc-linaro-6.5.0-2018.12-x86_64_aarch64-linux-gnu/bin:$PATH
            
            cov-configure --template --compiler aarch64-linux-gnu-gcc --comptype gcc
            cov-configure --template --compiler aarch64-linux-gnu-g++ --comptype g++
            cov-configure --comptype prefix --compiler ccache --template
            cov-build --dir ${build_cache_path} bash -ex XXX.sh
            cov-manage-emit --dir ${build_cache_path} add-other-hosts
            cov-analyze --dir ${build_cache_path} -force
            if [ ${COVERITY_UPLOAD} == 'true' ];then
                cov-commit-defects --dir ${build_cache_path} --url http://coverity-earth.hobot.cc:8080 --auth-key-file ${coverity_auth_key_path} --stream ${stream_path}
            fi
            cov-format-errors --dir ${build_cache_path} --html-output /home/jenkins/workspace/coverity/coverity_html
        '''
    }
}

def coverity_prepare(){
    sh '''
        coverity_tool_path='/home/ci-cache/synopsys/coverity/tools/'
        coverity_cache_path='/home/ci-cache/synopsys/coverity/build_cache/'
        repo_path=`echo ${gitlabSourceRepoSshUrl} | sed 's/.git//g' | sed 's/gitlab.hobot.cc://g'`
        build_cache_path='/home/jenkins/workspace/coverity_cache'                             
        cp ${build_cache_path}/output/FUNCTION.metrics.xml.gz .
        gunzip FUNCTION.metrics.xml.gz
        python2 ${coverity_tool_path}parse_mcc.py FUNCTION.metrics.xml 5
        
        cp ${build_cache_path}/output/summary.txt .
        cp -r /home/jenkins/workspace/coverity/coverity_html .
    '''
}
你好!要设置栅格数据缓存,可以按照以下步骤进行: 1. 首先,确保你已经安装并成功启动了 GeoServer。你可以在浏览器中访问 GeoServer 的管理界面。 2. 登录 GeoServer 的管理界面,使用管理员账户登录。 3. 在管理界面的左侧导航栏中,点击 "Tile Layers"(或 "瓦片图层")。 4. 在瓦片图层页面中,你可以看到所有已发布的栅格图层。选择你想要设置缓存的栅格图层。 5. 在栅格图层的详细信息页面中,点击 "Tile Caching"(或 "瓦片缓存")选项卡。 6. 在瓦片缓存选项卡中,你可以看到有关缓存设置的各种选项。根据你的需求进行配置。 - "Enabled"(启用):勾选此选项以启用瓦片缓存。 - "Cache Directory"(缓存目录):设置用于存储瓦片缓存文件的目录路径。 - "Disk Quota"(磁盘配额):设置磁盘配额,即瓦片缓存的最大磁盘使用量。 - "Resolutions"(分辨率):设置每个缩放级别的分辨率。 - "Grid Set"(网格集):选择用于生成瓦片的网格集。 - "Metatiling"(元瓦片):设置元瓦片大小,即内存中处理的瓦片块数量。 - 其他高级选项:根据需要进行配置。 7. 配置完成后,点击页面底部的 "Save"(保存)按钮以应用更改。 这样,你就成功设置了栅格数据的缓存。GeoServer 将会生成和存储瓦片缓存文件,以提高栅格数据的渲染性能。请注意,具体的配置选项可能会因 GeoServer 版本而有所不同,但基本原理是相同的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值