Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 Building with Skia Tutorial | |
| 2 =========================== | |
| 3 | |
| 4 dsinclair@chromium.org | |
| 5 | |
| 6 | |
| 7 This document describes the steps used to create an application that uses Skia. The assumptions are that you're using: | |
| 8 | |
| 9 * [git](http://git-scm.com) | |
| 10 * [gclient](https://code.google.com/p/gclient/) | |
| 11 * [gyp](https://code.google.com/p/gyp/) | |
| 12 * [ninja](http://martine.github.io/ninja/) | |
| 13 | |
| 14 I'm going to describe up to the point where we can build a simple application th at prints out an SkPaint. | |
| 15 | |
| 16 Overview | |
| 17 -------- | |
| 18 | |
| 19 1. Create remote repository. | |
| 20 1. Configure and sync using gclient. | |
| 21 1. Create DEPS file to pull in third party repositories. | |
| 22 1. Setup gitignore for directories pulled in from DEPS. | |
| 23 1. Configure GYP. | |
| 24 1. Setup GYP auto-run when gclient sync is executed. | |
| 25 | |
| 26 gclient setup | |
| 27 ------------- | |
| 28 The first step is to setup a remote git repo, take your pick of provider. In | |
| 29 my case, the repo is called UsingSkia and lives on | |
| 30 [bitbucket](https://bitbucket.org). | |
| 31 | |
| 32 With the remote repo created, we create a .gclient configuration file. The | |
| 33 gclient config command will write the file for us: | |
| 34 | |
| 35 $ gclient config --name=src https://bitbucket.org/dj2/usingskia.git | |
| 36 | |
| 37 This will create the following: | |
| 38 | |
| 39 solutions = [ | |
| 40 { "name" : "src", | |
| 41 "url" : "https://bitbucket.org/dj2/usingskia.git", | |
| 42 "deps_file" : "DEPS", | |
| 43 "managed" : True, | |
| 44 "custom_deps" : { | |
| 45 }, | |
| 46 "safesync_url": "", | |
| 47 }, | |
| 48 ] | |
| 49 cache_dir = None | |
| 50 | |
| 51 The name that we configured is the directory in which the repo will be checked | |
| 52 out. This is done by running gclient sync. There is a bit of magic that | |
| 53 gclient does around the url to determine if the repo is SVN or GIT. I've found | |
| 54 the use of ssh:// and the .git on the end seem to work to get the right SCM | |
| 55 type. | |
| 56 | |
| 57 $ gclient sync | |
|
jcgregorio
2015/01/07 13:53:23
4 leading spaces
| |
| 58 | |
| 59 This should execute a bunch of commands (and, in this case, may end with an | |
| 60 error because the repo was empty. That seems to be fine.) When finished, you | |
| 61 should have a src directory with your git repository checked out. | |
| 62 | |
| 63 DEPS | |
| 64 ---- | |
| 65 | |
| 66 With the repo created we can go ahead and create our src/DEPS file. The DEPS | |
| 67 file is used by gclient to checkout the dependent repositories of our | |
| 68 application. In this case, the Skia repository. | |
| 69 | |
| 70 Create a src/DEPS file with the following: | |
| 71 | |
| 72 ~~~~ | |
| 73 | |
| 74 vars = { | |
| 75 "skia_revision": "a6a8f00a3977e71dbce9da50a32c5e9a51c49285", | |
| 76 } | |
| 77 | |
| 78 deps = { | |
| 79 "src/third_party/skia/": | |
| 80 "http://skia.googlecode.com/skia.git@" + Var("skia_revision"), | |
| 81 } | |
| 82 | |
| 83 ~~~~ | |
| 84 | |
| 85 There are two sections to the `DEPS` file at the moment, `vars` and `deps`. | |
| 86 The `vars` sections defines variables we can use later in the file with the | |
| 87 `Var()` accessor. In this case, we define our root directory, a shorter name | |
| 88 for any googlecode repositories and a specific revision of Skia that we're | |
| 89 going to use. I've pinned to a specific version to insulate the application | |
| 90 from changes in the Skia tree. This lets us know that when someone checks out | |
| 91 the repo they'll be using the same version of Skia that we've built and tested | |
| 92 against. | |
| 93 | |
| 94 The `deps` section defines our dependencies. Currently we have one dependency | |
| 95 which we're going to checkout into the `src/third_party/skia` directory. | |
| 96 | |
| 97 Once the deps file is created, commit and push it to the remote repository. | |
| 98 Once done, we can use gclient to checkout our dependencies. | |
| 99 | |
| 100 $ gclient sync | |
| 101 | |
| 102 This should output a whole bunch of lines about files that are being added to | |
| 103 your project. This may also be a good time to create a `.gitignore` file. You | |
| 104 don't want to check the `third_party/skia directory` into your repository as | |
| 105 it's being managed by gclient. | |
| 106 | |
| 107 Now, we've run into a problem. Skia itself has a `DEPS` file which defines the | |
| 108 `third_party` libraries it needs to build. None of those dependencies are being | |
| 109 checked out so Skia will fail to build. | |
| 110 | |
| 111 The way I found around that is to add a second solution to the `.gclient` | |
| 112 file. This solution tells gclient about Skia and will pull in the needed | |
| 113 dependencies. I edited my `.gclient` file (created by the `gclient config` | |
| 114 command above) to look as follows: | |
| 115 | |
| 116 solutions = [ | |
| 117 { "name" : "src", | |
| 118 "url" : "https://bitbucket.org/dj2/usingskia.git", | |
| 119 "deps_file" : "DEPS", | |
| 120 "managed" : True, | |
| 121 "custom_deps" : { | |
| 122 }, | |
| 123 "safesync_url": "", | |
| 124 }, | |
| 125 { "name" : "src/third_party/skia", | |
| 126 "url" : "http://skia.googlecode.com/skia.git@a6a8f00a3977e71dbce 9da50a32c5e9a51c49285", | |
| 127 "deps_file" : "DEPS", | |
| 128 "managed" : True, | |
| 129 "custom_deps" : { | |
| 130 }, | |
| 131 "safesync_url": "", | |
| 132 }, | |
| 133 ] | |
| 134 cache_dir = None | |
| 135 | |
| 136 This is a little annoying at the moment since I've duplicated the repository | |
| 137 revision number in the `.gclient` file. I'm hoping to find a way to do this | |
| 138 through the `DEPS` file, but until then, this seems to work. | |
| 139 | |
| 140 With that done, re-run `gclient sync` and you should see a whole lot more | |
| 141 repositories being checked out. The | |
| 142 `src/third_party/skia/third_party/externals` directory should now be | |
| 143 populated. | |
| 144 | |
| 145 GYP | |
| 146 --- | |
| 147 | |
| 148 The final piece of infrastructure we need to set up is GYP. GYP is a build | |
| 149 system generator, in this project we're going to have it build ninja | |
| 150 configuration files. | |
| 151 | |
| 152 First, we need to add GYP to our project. We'll do that by adding a new entry | |
| 153 to the deps section of the `DEPS` file. | |
| 154 | |
| 155 "src/tools/gyp": | |
| 156 (Var("googlecode_url") % "gyp") + "/trunk@1700", | |
| 157 | |
| 158 As you can see, I'm going to put the library into `src/tools/gyp` and checkout | |
| 159 revision 1700 (note, the revision used here, 1700, was the head revision at | |
| 160 the time the `DEPS` file was written. You're probably safe to use the | |
| 161 tip-of-tree revision in your `DEPS` file). A quick `gclient sync` and we | |
| 162 should have everything checked out. | |
| 163 | |
| 164 In order to run GYP we'll create a wrapper script. I've called this | |
| 165 `src/build/gyp_using_skia`. | |
| 166 | |
| 167 ~~~~ | |
| 168 #!/usr/bin/python | |
| 169 import os | |
| 170 import sys | |
| 171 | |
| 172 script_dir = os.path.dirname(__file__) | |
| 173 using_skia_src = os.path.abspath(os.path.join(script_dir, os.pardir)) | |
| 174 | |
| 175 sys.path.insert(0, os.path.join(using_skia_src, 'tools', 'gyp', 'pylib')) | |
| 176 import gyp | |
| 177 | |
| 178 if __name__ == '__main__': | |
| 179 args = sys.argv[1:] | |
| 180 | |
| 181 if not os.environ.get('GYP_GENERATORS'): | |
| 182 os.environ['GYP_GENERATORS'] = 'ninja' | |
| 183 | |
| 184 args.append('--check') | |
| 185 args.append('-I%s/third_party/skia/gyp/common.gypi' % using_skia_src) | |
| 186 | |
| 187 args.append(os.path.join(script_dir, '..', 'using_skia.gyp')) | |
| 188 | |
| 189 print 'Updating projects from gyp files...' | |
| 190 sys.stdout.flush() | |
| 191 | |
| 192 sys.exit(gyp.main(args)) | |
| 193 ~~~~ | |
| 194 | |
| 195 Most of this is just setup code. The two interesting bits are: | |
| 196 | |
| 197 1. `args.append('-I%s/third_party/skia/gyp/common.gypi' % using_skia_src)` | |
| 198 1. `args.append(os.path.join(script_dir, '..', 'using_skia.gyp'))` | |
| 199 | |
| 200 In the case of 1, we're telling GYP to include (-I) the | |
| 201 `src/third_party/skia/gyp/common.gypi` file which will define necessary | |
| 202 variables for Skia to compile. In the case of 2, we're telling GYP that the | |
| 203 main configuration file for our application is `src/using_skia.gyp`. | |
| 204 | |
| 205 The `src/using_skia.gyp` file is as follows: | |
| 206 | |
| 207 ~~~~ | |
| 208 { | |
| 209 'targets': [ | |
| 210 { | |
| 211 'configurations': { | |
| 212 'Debug': { }, | |
| 213 'Release': { } | |
| 214 }, | |
| 215 'target_name': 'using_skia', | |
| 216 'type': 'executable', | |
| 217 'dependencies': [ | |
| 218 'third_party/skia/gyp/skia_lib.gyp:skia_lib' | |
| 219 ], | |
| 220 'include_dirs': [ | |
| 221 'third_party/skia/include/config', | |
| 222 'third_party/skia/include/core', | |
| 223 ], | |
| 224 'sources': [ | |
| 225 'app/main.cpp' | |
| 226 ], | |
| 227 'ldflags': [ | |
| 228 '-lskia', '-stdlib=libc++', '-std=c++11' | |
| 229 ], | |
| 230 'cflags': [ | |
| 231 '-Werror', '-W', '-Wall', '-Wextra', '-Wno-unused-parameter', '-g', '-O0 ' | |
| 232 ] | |
| 233 } | |
| 234 ] | |
| 235 } | |
| 236 ~~~~ | |
| 237 | |
| 238 There is a lot going on in there, I'll touch on some of the highlights. The | |
| 239 `configurations` section allows us to have different build flags for our `Debug` | |
| 240 and `Release` build (in this case they're the same, but I wanted to define | |
| 241 them.) The `target_name` defines the name of the build target which we'll | |
| 242 provide to ninja. It will also be the name of the executable that we build. | |
| 243 | |
| 244 The dependencies section lists our build dependencies. These will be built | |
| 245 before our sources are built. In this case, we depend on the `skia_lib` target | |
| 246 inside `third_party/skia/gyp/skia_lib.gyp`. | |
| 247 | |
| 248 The include_dirs will be added to the include path when our files are built. | |
| 249 We need to reference code in the config and core directories of Skia. | |
| 250 | |
| 251 `sources`, `ldflags` and `cflags` should be obvious. | |
| 252 | |
| 253 Our application is defined in `src/app/main.cpp` as: | |
| 254 | |
| 255 ~~~~ | |
| 256 #include "SkPaint.h" | |
| 257 #include "SkString.h" | |
| 258 | |
| 259 int main(int argc, char** argv) { | |
| 260 SkPaint paint; | |
| 261 paint.setColor(SK_ColorRED); | |
| 262 | |
| 263 SkString* str = new SkString(); | |
| 264 paint.toString(str); | |
| 265 | |
| 266 fprintf(stdout, "%s\n", str->c_str()); | |
| 267 | |
| 268 return 0; | |
| 269 } | |
| 270 ~~~~ | |
| 271 | |
| 272 We're just printing out an SkPaint to show that everything is linking correctly. | |
| 273 | |
| 274 Now, we can run: | |
| 275 | |
| 276 $ ./build/gyp_using_skia | |
| 277 | |
| 278 And, we get an error. Turns out, Skia is looking for a `find\_mac\_sdk.py` file in | |
| 279 a relative tools directory which doesn't exist. Luckily, that's easy to fix | |
| 280 with another entry in our DEPS file. | |
| 281 | |
| 282 "src/tools/": | |
| 283 File((Var("googlecode_url") % "skia") + "/trunk/tools/find_mac_sdk.py@" + | |
| 284 Var("skia_revision")), | |
| 285 | |
| 286 Here we using the `File()` function of `gclient` to specify that we're checking | |
| 287 out an individual file. Running `gclient sync` should pull the necessary file | |
| 288 into `src/tools`. | |
| 289 | |
| 290 With that, running `build/gyp\_using\_skia` should complete successfully. You | |
| 291 should now have an `out/` directory with a `Debug/` and `Release/` directory ins ide. | |
| 292 These correspond to the configurations we specified in `using\_skia.gyp`. | |
| 293 | |
| 294 With all that out of the way, if you run: | |
| 295 | |
| 296 $ ninja -C out/Debug using_skia | |
| 297 | |
| 298 The build should execute and you'll end up with an `out/Debug/using\_skia` which | |
| 299 when executed, prints out our SkPaint entry. | |
| 300 | |
| 301 Autorun GYP | |
| 302 ----------- | |
| 303 | |
| 304 One last thing, having to run `build/gyp\_using\_skia` after each sync is a bit of | |
| 305 a pain. We can fix that by adding a `hooks` section to our `DEPS` file. The `hoo ks` | |
| 306 section lets you list a set of hooks to execute after `gclient` has finished the | |
| 307 sync. | |
| 308 | |
| 309 hooks = [ | |
| 310 { | |
| 311 # A change to a .gyp, .gypi or to GYP itself should run the generator. | |
| 312 "name": "gyp", | |
| 313 "pattern": ".", | |
| 314 "action": ["python", "src/build/gyp_using_skia"] | |
| 315 } | |
| 316 ] | |
| 317 | |
| 318 Adding the above to the end of DEPS and running gclient sync should show the | |
| 319 GYP files being updated at the end of the sync procedure. | |
| 320 | |
| OLD | NEW |