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 |