【转】从零开始手敲次世代游戏引擎(二)

本文转自知乎,由超级码农陈文礼大神编写,已获原作者授权,原文链接: https://zhuanlan.zhihu.com/p/28598462

开始摆脱Visual Studio,建立独立的Toolchain

上一篇我们写了一个最基本的Hello Engine,并用Visual Studio的命令行工具,cl.exe进行了编译。

然而,Visual Studio只能在Windows上面使用。而且Visual Studio对C/C++进行了很多非标准的扩展。因此基于Visual Studio写出来的程序,除非你写的时候就很清楚哪些可以用哪些不可以用,否则基本是不可以移植到别的平台的。因为Windows并不是一个POSIX (POSIX - Wikipedia)系统,也就是说是一个非常不“标准”的系统。基于这样的系统的API写出来的程序基本只能跑在这个系统上。

我打算让这个手打引擎跑在所有我可以接触到的平台上。目前我可以接触到的平台有:Windows/Linux/PS4/PSV/Android/IOS

所以我需要打造一个独立于特定平台的编译工具包,也就是Toolchain。

目前在开源领域用得比较多的Toolchain是GCC和Clang。GCC历史比较长,很多开源软件,包括Linux内核都是GCC编译的。但厚重的历史也使其很臃肿,里面包括很多已经死掉的东西。而Clang则较年轻,现在也比较流行。

另外,PS4的编译器就是基于Clang的。AMD的OpenGPU计划,以及Vulkan图形API等也是基于Clang的。苹果的最新开发平台一样是基于Clang的。所以,我选择Clang。

准备编译Clang的环境

Clang的项目页面在Clang - Getting Started

首先我们按照Clang项目页面的提示,在Windows上面安装Subversion,这个是获取Clang源代码用的。我推荐安装TortoiseSVN,这个相对比较好用。注意命令行工具缺省是不安装的,需要手工勾选安装。

Home · TortoiseSVN

然后是CMake。我们在Visual Studio里面建立工程的时候,会自动创建Solution和Project文件来进行代码的组织管理和编译选项的存储。然而,这些同样是只有Visual Studio才能使用的文件格式。在Linux等平台上一般是使用make,或者GNU版的make:gmake。make是依靠一个叫做Makefile的文件来存储项目文件清单和编译选项的。可以直接手写,但是文件多了一般我们就希望自动生成。况且,在不同平台上面,虽然都有C/C++编译器,能够编译C/C++代码,但是各种库的头文件、静态链接库、动态链接库的存储位置,甚至是名字都会有很微妙的差异。所以,如果直接手写Makefile,那么结果就是我们需要为每个平台单独写一个。有一些早期GNU软件就是这样的。这很不利于管理。比如我们添加了一个C++文件,那么我们就需要改所有不同版本的Makefile。

所以有一个工具叫Auto Tools,包括automake autoconf等一系列工具。这些工具可以根据一个叫做http://Makefile.am的模板(与Makefile的区别是里面基本只写项目里的文件,因为这些文件的位置是我们自己可以控制的)自动生成Makefile。这些工具可以为我们自动检测一些常见平台的差异,并在生成的Makefile里面消除这些差异。

然而这个Auto Tools本身也是足够复杂的,使用起来并不是很方便,况且不支持Windows平台。有兴趣的可以参考

Autotools Introduction

CMake是近年兴起的新秀,支持包括Windows在内的诸多平台,使用也比Auto Tools要方便不少。只需要写个CMakelists.txt就可以了。CMake在这里下载。

CMake

安装的时候,同样需要注意,因为我们工作在命令行,需要让安装程序设置环境参数,如上图。否则在命令行会找不到cmake。

接下来是Python。注意Python 2和Python 3是不兼容的。Python 2很古老但是久经考验,Python 3比较新,但是还不是很成熟。我们这里需要的是Python 2.7(因为Clang的test case是2.7接口的)。话说Python近年随着阿尔法?大红大紫,因为人工智能领域用Python用得很多。一般来说,越是偏应用方向的(比如人工智能算法研究),越是用高阶的语言(脚本),避免在本来关心的事物之外花费时间。

Download Python

当然,我们这里安装Python是为了跑Clang的测试case,确认我们自己编译出的Clang功能正常。这个步骤是十分重要的。因为如果是编译器的bug带来的问题,一般都可以轻易将码农坑在里面几个月出不来。比如一个变量明显代入了1,后面读出来偏偏变成了2...(CPU Cache控制问题)这种问题是最难查出来的问题之一。

最后是GnuWin32 Tools,这是一组开源命令行工具。Linux什么的都是自带或者可以很方便地安装的。Windows上面就需要下载安装:

GetGnuWin32 - Maintaining a Gnuwin32 Package archive

这些工具数量众多,我们这里主要也是为了跑Clang的测试Case,就不一一展开了。

需要注意的是,网页上能下载的东西只是装了个下载器,装完之后需要进入安装目标目录,执行download.bat和install.bat完成安装。之后需要更改环境变量PATH,保证在我们的命令行里面可以找到这些工具。(具体路径请根据你安装的路径修改)

关于如何改Windows的环境变量,参考下面

http://jingyan.baidu.com/article/8ebacdf02d3c2949f65cd5d0.html

好了。现在我们重新启动命令行,来使修改的环境变量生效。(命令行窗口会一直保持启动的时候的环境变量,所以改了环境变量之后需要重启命令行才能反映出我们的修改)

输入svn help,看到类似下面的输出,说明subversion安装OK了:

C:\Users\Tim.AzureAD\Source>svn help
usage: svn <subcommand> [options] [args]
Subversion command-line client.
Type 'svn help <subcommand>' for help on a specific subcommand.
Type 'svn --version' to see the program version and RA modules
  or 'svn --version --quiet' to see just the version number.

Most subcommands take file and/or directory arguments, recursing
on the directories.  If no arguments are supplied to such a
command, it recurses on the current directory (inclusive) by default.

Available subcommands:
   add
   ...

输入python,看到类似下面的输出,则说明python安装OK了:

C:\Users\Tim.AzureAD>python
Python 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:53:40) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>

按Ctrl+Z,回车,退出python交互模式。

输入grep,看到类似下面的输出,则说明GnuWin32工具也安装成功了。

C:\Users\Tim.AzureAD>grep
Usage: grep [OPTION]... PATTERN [FILE]...
Try `grep --help' for more information.

开始编译Clang

Clang是基于LLVM的。所谓LLVM,就是一个小小的虚拟机。这个虚拟机抽象了不同的硬件平台,如x86/arm/mips等。最近还抽象了GPU。有点像Java的VM,但是又和Java的VM很不同。Java的VM是比较高层的,它的byte code包括很多硬件平台并不能直接支持的功能。而LLVM的byte code则是更加接近硬件(CPU/GPU)的实际功能,只不过它是独立于任何一个具体硬件存在的。非常简单粗糙地比喻的话,各种CPU/GPU就好比各个地方的人,说的是各个地方的方言;而LLVM的byte code则有些像普通话,与方言有着比较类似1对1的对应关系。(当然严格地来讲并不是这么回儿事情)

所以,首先需要签出LLVM的代码,如下操作:

C:\Users\Tim.AzureAD\Source>svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm

Clang是作为LLVM的一个前端,即,把C/C++翻译为LLVM可以懂的byte code的工具。LLVM再把byte code翻译为具体的机器指令。执行下面的命令签出Clang的代码并放在LLVM妥当的位置:

C:\Users\Tim.AzureAD\Source>cd llvm\tools

C:\Users\Tim.AzureAD\Source\llvm\tools>svn co http://llvm.org/svn/llvm-project/cfe/trunk clang

还记得我们前面编译的main.c吗?编译产生的输出,也就是中间文件main.obj,target文件main.exe都是和main.c在一个目录里的。

C:\Users\Tim.AzureAD\Source\Repos\GameEngineFromScratch>dir
 驱动器 C 中的卷是 OS
 卷的序列号是 38A2-CBDD

 C:\Users\Tim.AzureAD\Source\Repos\GameEngineFromScratch 的目录

2017/08/18  09:31    <DIR>          .
2017/08/18  09:31    <DIR>          ..
2017/08/18  08:30               302 .gitignore
2017/08/18  08:30             1,088 LICENSE
2017/08/18  09:29                71 main.c
2017/08/18  09:31            97,280 main.exe
2017/08/18  09:31             1,285 main.obj
2017/08/18  08:30               103 README.md
               6 个文件        100,129 字节
               2 个目录 883,355,103,232 可用字节

对于简单的程序我们可以这样。对于大型软件来说,如果我们这样编译,成千上万的中间文件会把整个目录搞得混乱不堪,非常不利于管理。最为关键的是,如果我们的代码支持一些编译选项,可以从一套代码里编译出不同的版本(比如最常见的,Debug版和Release版),那么不同编译选项编译所生成的中间文件就会相互覆盖,最后搞得编译器也弄不清楚哪些文件编译过,是怎么编译的(按照什么选项编译的)。在我们码农的日常当中,如果我们遇到了一个项目第一次编译得过,第二次开始就出错,有的时候clean了重新编译也没用,那么多半就是这个原因了。

这种编译方式老外叫做"build in the (source) tree",这是不良的习惯。我们应该改掉。推荐的是“build outside the (source) tree”

所以让我们从llvm\tools这个目录出去,然后建立一个build目录,专门用来保存编译过程当中生成的文件。

C:\Users\Tim.AzureAD\Source\llvm\tools>cd ..\..

C:\Users\Tim.AzureAD\Source>mkdir build

C:\Users\Tim.AzureAD\Source>cd build

C:\Users\Tim.AzureAD\Source\build>

因为我们现在电脑上还只有Visual Studio所提供的编译工具,所以我们需要使用CMake工具来生成Visual Studio所需的Solution文件和Project文件,以便使用Visual Studio来编译LLVM

C:\Users\Tim.AzureAD\Source\build>cmake -G "Visual Studio 15" ..\llvm

-G "Visual Studio 15" 表示生成Visual Studio 2017用的项目文件。为什么叫"Visual Studio 15",这是因为在Visual Studio 6之后,微软改变了产品命名方式,Visual Studio 7叫Visual Studio .NET了。后面的版本更是,一会儿差一年一会儿差两年的。但是实际上他们内部仍然继续着这个序号,证据就是你看Windows里面的注册表当中的信息,就知道这个序号仍然在继续。(Office也是类似)

所以,从6开始数,Visual Studio 2017正好是15,Visual Studio 2015是14,Visual Studio 2013则是12。(嗯?13呢?被吃掉了?估计是13这个数字风水不好。。。)

如果记不住,可以用cmake --help命令查看:

C:\Users\Tim.AzureAD\Source\Repos\GameEngineFromScratch>cmake --help
Usage

  cmake [options] <path-to-source>
  cmake [options] <path-to-existing-build>

Specify a source directory to (re-)generate a build system for it in the
current working directory.  Specify an existing build directory to
re-generate its build system.

Options
  -C <initial-cache>           = Pre-load a script to populate the cache.
  -D <var>[:<type>]=<value>    = Create a cmake cache entry.
  -U <globbing_expr>           = Remove matching entries from CMake cache.
  -G <generator-name>          = Specify a build system generator.
  -T <toolset-name>            = Specify toolset name if supported by
                                 generator.
  -A <platform-name>           = Specify platform name if supported by
                                 generator.
  -Wdev                        = Enable developer warnings.
  -Wno-dev                     = Suppress developer warnings.
  -Werror=dev                  = Make developer warnings errors.
  -Wno-error=dev               = Make developer warnings not errors.
  -Wdeprecated                 = Enable deprecation warnings.
  -Wno-deprecated              = Suppress deprecation warnings.
  -Werror=deprecated           = Make deprecated macro and function warnings
                                 errors.
  -Wno-error=deprecated        = Make deprecated macro and function warnings
                                 not errors.
  -E                           = CMake command mode.
  -L[A][H]                     = List non-advanced cached variables.
  --build <dir>                = Build a CMake-generated project binary tree.
  -N                           = View mode only.
  -P <file>                    = Process script mode.
  --find-package               = Run in pkg-config like mode.
  --graphviz=[file]            = Generate graphviz of dependencies, see
                                 CMakeGraphVizOptions.cmake for more.
  --system-information [file]  = Dump information about this system.
  --debug-trycompile           = Do not delete the try_compile build tree.
                                 Only useful on one try_compile at a time.
  --debug-output               = Put cmake in a debug mode.
  --trace                      = Put cmake in trace mode.
  --trace-expand               = Put cmake in trace mode with variable
                                 expansion.
  --trace-source=<file>        = Trace only this CMake file/module.  Multiple
                                 options allowed.
  --warn-uninitialized         = Warn about uninitialized values.
  --warn-unused-vars           = Warn about unused variables.
  --no-warn-unused-cli         = Don't warn about command line options.
  --check-system-vars          = Find problems with variable usage in system
                                 files.
  --help,-help,-usage,-h,-H,/? = Print usage information and exit.
  --version,-version,/V [<f>]  = Print version number and exit.
  --help-full [<f>]            = Print all help manuals and exit.
  --help-manual <man> [<f>]    = Print one help manual and exit.
  --help-manual-list [<f>]     = List help manuals available and exit.
  --help-command <cmd> [<f>]   = Print help for one command and exit.
  --help-command-list [<f>]    = List commands with help available and exit.
  --help-commands [<f>]        = Print cmake-commands manual and exit.
  --help-module <mod> [<f>]    = Print help for one module and exit.
  --help-module-list [<f>]     = List modules with help available and exit.
  --help-modules [<f>]         = Print cmake-modules manual and exit.
  --help-policy <cmp> [<f>]    = Print help for one policy and exit.
  --help-policy-list [<f>]     = List policies with help available and exit.
  --help-policies [<f>]        = Print cmake-policies manual and exit.
  --help-property <prop> [<f>] = Print help for one property and exit.
  --help-property-list [<f>]   = List properties with help available and
                                 exit.
  --help-properties [<f>]      = Print cmake-properties manual and exit.
  --help-variable var [<f>]    = Print help for one variable and exit.
  --help-variable-list [<f>]   = List variables with help available and exit.
  --help-variables [<f>]       = Print cmake-variables manual and exit.

Generators

The following generators are available on this platform:
  Visual Studio 15 2017 [arch] = Generates Visual Studio 2017 project files.
                                 Optional [arch] can be "Win64" or "ARM".
  Visual Studio 14 2015 [arch] = Generates Visual Studio 2015 project files.
                                 Optional [arch] can be "Win64" or "ARM".
  Visual Studio 12 2013 [arch] = Generates Visual Studio 2013 project files.
                                 Optional [arch] can be "Win64" or "ARM".
  Visual Studio 11 2012 [arch] = Generates Visual Studio 2012 project files.
                                 Optional [arch] can be "Win64" or "ARM".
  Visual Studio 10 2010 [arch] = Generates Visual Studio 2010 project files.
                                 Optional [arch] can be "Win64" or "IA64".
  Visual Studio 9 2008 [arch]  = Generates Visual Studio 2008 project files.
                                 Optional [arch] can be "Win64" or "IA64".
  Visual Studio 8 2005 [arch]  = Deprecated.  Generates Visual Studio 2005
                                 project files.  Optional [arch] can be
                                 "Win64".
  Borland Makefiles            = Generates Borland makefiles.
  NMake Makefiles              = Generates NMake makefiles.
  NMake Makefiles JOM          = Generates JOM makefiles.
  Green Hills MULTI            = Generates Green Hills MULTI files
                                 (experimental, work-in-progress).
  MSYS Makefiles               = Generates MSYS makefiles.
  MinGW Makefiles              = Generates a make file for use with
                                 mingw32-make.
  Unix Makefiles               = Generates standard UNIX makefiles.
  Ninja                        = Generates build.ninja files.
  Watcom WMake                 = Generates Watcom WMake makefiles.
  CodeBlocks - MinGW Makefiles = Generates CodeBlocks project files.
  CodeBlocks - NMake Makefiles = Generates CodeBlocks project files.
  CodeBlocks - NMake Makefiles JOM
                               = Generates CodeBlocks project files.
  CodeBlocks - Ninja           = Generates CodeBlocks project files.
  CodeBlocks - Unix Makefiles  = Generates CodeBlocks project files.
  CodeLite - MinGW Makefiles   = Generates CodeLite project files.
  CodeLite - NMake Makefiles   = Generates CodeLite project files.
  CodeLite - Ninja             = Generates CodeLite project files.
  CodeLite - Unix Makefiles    = Generates CodeLite project files.
  Sublime Text 2 - MinGW Makefiles
                               = Generates Sublime Text 2 project files.
  Sublime Text 2 - NMake Makefiles
                               = Generates Sublime Text 2 project files.
  Sublime Text 2 - Ninja       = Generates Sublime Text 2 project files.
  Sublime Text 2 - Unix Makefiles
                               = Generates Sublime Text 2 project files.
  Kate - MinGW Makefiles       = Generates Kate project files.
  Kate - NMake Makefiles       = Generates Kate project files.
  Kate - Ninja                 = Generates Kate project files.
  Kate - Unix Makefiles        = Generates Kate project files.
  Eclipse CDT4 - NMake Makefiles
                               = Generates Eclipse CDT 4.0 project files.
  Eclipse CDT4 - MinGW Makefiles
                               = Generates Eclipse CDT 4.0 project files.
  Eclipse CDT4 - Ninja         = Generates Eclipse CDT 4.0 project files.
  Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files.

好了,然后用下面的命令build生成的Solution。 (注意要在Visual Studio的命令行里面。也就是开始菜单里面的Developer Command Prompt)当然你也可以双击LLVM.sln打开Visual Studio的IDE进行编译。效果其实一样的。

C:\Users\Tim.AzureAD\Source\build>msbuild LLVM.sln

这个编译看机器性能。我在i7 8核的SSD机器上大概1个半小时。


(注意:采用上面的方法编译出的是x86的Debug版本。如果需要编译x64的Release版本,请如下使用CMake):

cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_BUILD_TYPE=Release -Thost=x64 ..\llvm
msbuild ALL_BUILD.vcxproj /V:m /p:Platform=x64 /p:Configuration=Release /t:rebuild

编译完成之后,我们来测试我们编译出的clang是否有问题。首先我们需要将生成物的目录加入环境变量PATH,以便在命令行能够找到它:(目录请根据你的本地实际情况修改)

重启命令行,检查是否可以找到clang

C:\Users\Tim.AzureAD\Source>clang -v
clang version 6.0.0 (trunk 311143)
Target: i686-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Users\Tim.AzureAD\Source\build\Debug\bin
Found CUDA installation: /Program Files/NVIDIA GPU Computing Toolkit/CUDA/v8.0, version 8.0

最后一行CUDA是我的环境里面别的事情安装的。与目前无关。没有安装的应该看不到这一行。

然后确保我们目前是处于LLVM的顶级目录,就是下面有llvm和build这两个目录的那一级目录,执行下面的命令:

C:\Users\Tim.AzureAD\Source>python.exe llvm\utils\lit\lit.py -sv --param=build_mode=Win32 --param=build_config=Debug --param=clang_site_config=build\tools\clang\test\lit.site.cfg llvm\tools\clang\test

我这里的环境是执行会失败,python抱怨找不到一些测试用的程序。需要修改build\tools\clang\test\lit.site.cfg

原来的版本:

## Autogenerated from C:/Users/Tim.AzureAD/Source/llvm/tools/clang/test/lit.site.cfg.in
## Do not edit!

import sys

config.llvm_src_root = "C:/Users/Tim.AzureAD/Source/llvm"
config.llvm_obj_root = "C:/Users/Tim.AzureAD/Source/build"
config.llvm_tools_dir = "C:/Users/Tim.AzureAD/Source/build/%(build_mode)s/bin"
config.llvm_libs_dir = "C:/Users/Tim.AzureAD/Source/build/%(build_mode)s/lib"
config.llvm_shlib_dir = "C:/Users/Tim.AzureAD/Source/build/%(build_mode)s/bin"
config.llvm_plugin_ext = ".dll"
config.lit_tools_dir = ""
config.clang_obj_root = "C:/Users/Tim.AzureAD/Source/build/tools/clang"
config.clang_src_dir = "C:/Users/Tim.AzureAD/Source/llvm/tools/clang"
config.clang_tools_dir = "C:/Users/Tim.AzureAD/Source/build/%(build_mode)s/bin"
config.host_triple = "i686-pc-win32"
config.target_triple = "i686-pc-win32"
config.llvm_use_sanitizer = ""
config.have_zlib = 0
config.clang_arcmt = 1
config.clang_default_cxx_stdlib = ""
config.clang_staticanalyzer = 1
config.clang_staticanalyzer_z3 = ""
config.clang_examples = 0
config.enable_shared = 0
config.enable_backtrace = 1
config.host_arch = "AMD64"
config.enable_abi_breaking_checks = ""

改为

## Autogenerated from C:/Users/Tim.AzureAD/Source/llvm/tools/clang/test/lit.site.cfg.in
## Do not edit!

import sys

config.llvm_src_root = "C:/Users/Tim.AzureAD/Source/llvm"
config.llvm_obj_root = "C:/Users/Tim.AzureAD/Source/build"
config.llvm_tools_dir = "C:/Users/Tim.AzureAD/Source/build/%(build_config)s/bin"
config.llvm_libs_dir = "C:/Users/Tim.AzureAD/Source/build/%(build_config)s/lib"
config.llvm_shlib_dir = "C:/Users/Tim.AzureAD/Source/build/%(build_config)s/bin"
config.llvm_plugin_ext = ".dll"
config.lit_tools_dir = ""
config.clang_obj_root = "C:/Users/Tim.AzureAD/Source/build/tools/clang"
config.clang_src_dir = "C:/Users/Tim.AzureAD/Source/llvm/tools/clang"
config.clang_tools_dir = "C:/Users/Tim.AzureAD/Source/build/%(build_config)s/bin"
config.host_triple = "i686-pc-win32"
config.target_triple = "i686-pc-win32"
config.llvm_use_sanitizer = ""
config.have_zlib = 0
config.clang_arcmt = 1
config.clang_default_cxx_stdlib = ""
config.clang_staticanalyzer = 1
config.clang_staticanalyzer_z3 = ""
config.clang_examples = 0
config.enable_shared = 0
config.enable_backtrace = 1
config.host_arch = "AMD64"
config.enable_abi_breaking_checks = ""

就是把所有的%(build_mode)改为%(build_config)

如果是用vim修改,可以用“:%s/build_mode/build_config/g”这条命令一次修改完毕。

感觉上应该是不同的Visual Studio对于项目文件当中Output目录宏展开的方式不同导致的。

测试正常执行的样子是这样的:

C:\Users\Tim.AzureAD\Source>python llvm\utils\lit\lit.py -sv --param=build_mode=Win32 --param=build_config=Debug --param=clang_site_config=build\tools\clang\test\lit.site.cfg llvm\tools\clang\test
lit.py: C:/Users/Tim.AzureAD/Source/llvm/tools/clang/test/lit.cfg:200: note: using clang: 'C:/Users/Tim.AzureAD/Source/build/Debug/bin/clang.EXE'
lit.py: C:\Users\Tim.AzureAD\Source\llvm\utils\lit\lit\discovery.py:190: warning: test suite 'Clang-Unit' contained no tests
-- Testing: 9208 tests, 8 threads --
********************
Testing: 0 .. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90..
Testing Time: 843.01s
********************
Failing Tests (2):
    Clang :: Driver/offloading-interoperability.c
    Clang :: Driver/openmp-offload-gpu.c

  Expected Passes    : 9077
  Expected Failures  : 24
  Unsupported Tests  : 105
  Unexpected Failures: 2

1 warning(s) in tests.

星号当中的是进度条。在i7 8核心的机器上大约需要10分钟左右。

我这里执行的过程当中出现一些CUDA相关的错误,应该是版本不匹配(我的是CUDA 8.0,比较新)导致,可以无视。

用新的Toolchain编译我们的Hello Engine

C:\Users\Tim.AzureAD\Source\Repos\GameEngineFromScratch>clang main.c
main.c:3:1: warning: return type of 'main' is not 'int' [-Wmain-return-type]
void main() {
^
main.c:3:1: note: change return type to 'int'
void main() {
^~~~
int
1 warning generated.

可以看到,在Visual Studio下面编译完全没有问题的代码,在clang下面出现了warning。所以,我们需要尽早摆脱微软的安乐窝。(_)

(-- EOF -- )

参考引用

  1. Clang - Getting Started

本作品采用知识共享署名 4.0 国际许可协议进行许可。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏GuZhenYin

用SignalR 2.0开发客服系统[系列5:使用SignalR的中文简体语言包和其他技术点]

前言 交流群:195866844 目录: 用SignalR 2.0开发客服系统[系列1:实现群发通讯] 用SignalR 2.0开发客服系统[系列2:实现聊天室...

24790
来自专栏加米谷大数据

技术分享 | kafka的使用场景以及生态系统

kafka的使用场景 今天介绍一些关于Apache kafka 流行的使用场景。这些领域的概述 消息 kafka更好的替换传统的消息系统,消息系统被用于各种场景...

1K80
来自专栏xingoo, 一个梦想做发明家的程序员

Elasticsearch推荐插件篇(head,sense,marvel)

安装head head插件可以用来快速查看elasticsearch中的数据概况以及非全量的数据,也支持控件化查询和rest请求,但是体验都不是很好。 一般就用...

38170
来自专栏魏琼东

AgileEAS.NET SOA 中间件平台.Net Socket通信框架-介绍

     AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平...

13100
来自专栏FreeBuf

初窥卡巴斯基ARK读取MBR

LONG LONG LONG AGO就发现通过Hook磁盘端口驱动程序中的IRP_MJ_SCSI派遣函数方式过不了KB了,最近又遇到这个问题就想借此机会分析一下...

12260
来自专栏c#开发者

数据驱动开发For Silverlight WCF RIA1.0 三步曲

数据驱动开发For Silverlight WCF RIA1.0 三步曲 WCF RIA 1.0的正式发布,让Silverlight开发业务应用系统变得更加简...

28640
来自专栏杨建荣的学习笔记

通过shell脚本批量验证dataguard的有效性(r7笔记第96天)

我们假设一个场景,当你接触到一个新的环境,我们需要了解这个数据库是否为RAC,是否有备库。 如果有备库,那么问题来了,如果想去验证备库的状态是否有效,是否及时应...

29760
来自专栏GuZhenYin

一步步学习EF Core(3.EF Core2.0路线图)

前言 这几天一直在研究EF Core的官方文档,暂时没有发现什么比较新的和EF6.x差距比较大的东西. 不过我倒是发现了EF Core的路线图更新了,下面我们就...

25890
来自专栏一只程序汪的自我修养

半个小时教你写一个装(bi)逼(she)之地图搜租房

首先需要一个Python3环境,怎么准备我就不多说了,实在不会的出门右转看一下廖雪峰老师的博客.

453100
来自专栏walterlv - 吕毅的博客

如何在单元测试中使用 Dispatcher.Invoke/InvokeAsync?

发布于 2017-11-07 13:02 更新于 2018-08...

18110

扫码关注云+社区

领取腾讯云代金券