1"use strict";(self.webpackChunkelectronjs=self.webpackChunkelectronjs||[]).push([["32900"],{94704(e,n,i){i.r(n),i.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>c,frontMatter:()=>s,metadata:()=>t,toc:()=>h});var t=i(50128),r=i(74848),o=i(28453);let s={title:"Electron Internals: Building Chromium as a Library",date:new Date("2017-03-03T00:00:00.000Z"),authors:"zcbenz",slug:"electron-internals-building-chromium-as-a-library",tags:["internals"]},a,l={authorsImageUrls:[void 0]},h=[{value:"Using CEF",id:"using-cef",level:2},{value:"Building as part of Chromium",id:"building-as-part-of-chromium",level:2},{value:"Building Chromium as a single shared library",id:"building-chromium-as-a-single-shared-library",level:2},{value:"Filtering exported symbols",id:"filtering-exported-symbols",level:2},{value:"Component build",id:"component-build",level:2},{value:"Shipping raw binaries",id:"shipping-raw-binaries",level:2},{value:"The <code>gn</code> update",id:"the-gn-update",level:2},{value:"Summary",id:"summary",level:2}];function d(e){let n={a:"a",code:"code",h2:"h2",hr:"hr",p:"p",...(0,o.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.p,{children:"Electron is based on Google's open-source Chromium, a project that is not\nnecessarily designed to be used by other projects. This post introduces\nhow Chromium is built as a library for Electron's use, and how the build\nsystem has evolved over the years."}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h2,{id:"using-cef",children:"Using CEF"}),"\n",(0,r.jsx)(n.p,{children:"The Chromium Embedded Framework (CEF) is a project that turns Chromium into\na library, and provides stable APIs based on Chromium's codebase. Very\nearly versions of Atom editor and NW.js used CEF."}),"\n",(0,r.jsx)(n.p,{children:"To maintain a stable API, CEF hides all the details of Chromium\nand wraps Chromium's APIs with its own interface. So when we needed to\naccess underlying Chromium APIs, like integrating Node.js into web pages, the\nadvantages of CEF became blockers."}),"\n",(0,r.jsx)(n.p,{children:"So in the end both Electron and NW.js switched to using Chromium's APIs\ndirectly."}),"\n",(0,r.jsx)(n.h2,{id:"building-as-part-of-chromium",children:"Building as part of Chromium"}),"\n",(0,r.jsx)(n.p,{children:"Even though Chromium does not officially support outside projects, the codebase\nis modular and it is easy to build a minimal browser based on Chromium. The core\nmodule providing the browser interface is called Content Module."}),"\n",(0,r.jsxs)(n.p,{children:["To develop a project with Content Module, the easiest way is to build the\nproject as part of Chromium. This can be done by first checking out Chromium's\nsource code, and then adding the project to Chromium's ",(0,r.jsx)(n.code,{children:"DEPS"})," file."]}),"\n",(0,r.jsx)(n.p,{children:"NW.js and very early versions of Electron are using this way for building."}),"\n",(0,r.jsx)(n.p,{children:"The downside is, Chromium is a very large codebase and requires very powerful\nmachines to build. For normal laptops, that can take more than 5 hours.\nSo this greatly impacts the number of developers that can contribute to the\nproject, and it also makes development slower."}),"\n",(0,r.jsx)(n.h2,{id:"building-chromium-as-a-single-shared-library",children:"Building Chromium as a single shared library"}),"\n",(0,r.jsx)(n.p,{children:"As a user of Content Module, Electron does not need to modify Chromium's code\nunder most cases, so an obvious way to improve the building of Electron is to\nbuild Chromium as a shared library, and then link with it in Electron. In this\nway developers no longer need to build all off Chromium when contributing to\nElectron."}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.a,{href:"https://github.com/electron/libchromiumcontent",children:"libchromiumcontent"})," project was created by\n",(0,r.jsx)(n.a,{href:"https://github.com/aroben",children:"@aroben"})," for this purpose. It builds the Content\nModule of Chromium as a shared library, and then provides Chromium's headers\nand prebuilt binaries for download. The code of the initial version of\nlibchromiumcontent can be found ",(0,r.jsx)(n.a,{href:"https://github.com/electron/libchromiumcontent/tree/873daa8c57efa053d48aa378ac296b0a1206822c",children:"in this link"}),"."]}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.a,{href:"https://github.com/electron/brightray",children:"brightray"})," project was also born as part of libchromiumcontent,\nwhich provides a thin layer around Content Module."]}),"\n",(0,r.jsx)(n.p,{children:"By using libchromiumcontent and brightray together, developers can\nquickly build a browser without getting into the details of building Chromium.\nAnd it removes the requirement of a fast network and powerful machine for building\nthe project."}),"\n",(0,r.jsxs)(n.p,{children:["Apart from Electron, there were also other Chromium-based projects built in this\nway, like the ",(0,r.jsx)(n.a,{href:"https://www.quora.com/Is-Breach-Browser-still-in-development",children:"Breach browser"}),"."]}),"\n",(0,r.jsx)(n.h2,{id:"filtering-exported-symbols",children:"Filtering exported symbols"}),"\n",(0,r.jsx)(n.p,{children:"On Windows there is a limitation of how many symbols one shared library can\nexport. As the codebase of Chromium grew, the number of symbols exported in\nlibchromiumcontent soon exceeded the limitation."}),"\n",(0,r.jsxs)(n.p,{children:["The solution was to filter out unneeded symbols when generating the DLL file.\nIt worked by ",(0,r.jsxs)(n.a,{href:"https://github.com/electron/libchromiumcontent/pull/11/commits/85ca0f60208eef2c5013a29bb4cf3d21feb5030b",children:["providing a ",(0,r.jsx)(n.code,{children:".def"})," file to the linker"]}),", and then using\na script to ",(0,r.jsx)(n.a,{href:"https://github.com/electron/libchromiumcontent/pull/47/commits/d2fed090e47392254f2981a56fe4208938e538cd",children:"judge whether symbols under a namespace should be exported"}),"."]}),"\n",(0,r.jsx)(n.p,{children:"By taking this approach, though Chromium kept adding new exported symbols,\
1nlibchromiumcontent could still generate shared library files by stripping more\nsymbols."}),"\n",(0,r.jsx)(n.h2,{id:"component-build",children:"Component build"}),"\n",(0,r.jsx)(n.p,{children:"Before talking about the next steps taken in libchromiumcontent, it is important\nto introduce the concept of component build in Chromium first."}),"\n",(0,r.jsx)(n.p,{children:"As a huge project, the linking step takes very long in Chromium when building.\nNormally when a developer makes a small change, it can take 10 minutes to see the\nfinal output. To solve this, Chromium introduced component build, which builds\neach module in Chromium as separated shared libraries, so the time spent in the\nfinal linking step becomes unnoticeable."}),"\n",(0,r.jsx)(n.h2,{id:"shipping-raw-binaries",children:"Shipping raw binaries"}),"\n",(0,r.jsx)(n.p,{children:"With Chromium continuing to grow, there were so many exported symbols in\nChromium that even the symbols of Content Module and Webkit were more than the\nlimitation. It was impossible to generate a usable shared library by simply\nstripping symbols."}),"\n",(0,r.jsxs)(n.p,{children:["In the end, we had to ",(0,r.jsx)(n.a,{href:"https://github.com/electron/libchromiumcontent/pull/98",children:"ship the raw binaries of Chromium"})," instead of\ngenerating a single shared library."]}),"\n",(0,r.jsxs)(n.p,{children:["As introduced earlier there are two build modes in Chromium. As a result of\nshipping raw binaries, we have to ship two different distributions of binaries\nin libchromiumcontent. One is called ",(0,r.jsx)(n.code,{children:"static_library"})," build, which includes\nall static libraries of each module generated by the normal build of Chromium.\nThe other is ",(0,r.jsx)(n.code,{children:"shared_library"}),", which includes all shared libraries of each\nmodule generated by the component build."]}),"\n",(0,r.jsxs)(n.p,{children:["In Electron, the Debug version is linked with the ",(0,r.jsx)(n.code,{children:"shared_library"})," version of\nlibchromiumcontent, because it is small to download and takes little time\nwhen linking the final executable. And the Release version of Electron is\nlinked with the ",(0,r.jsx)(n.code,{children:"static_library"})," version of libchromiumcontent, so the compiler\ncan generate full symbols which are important for debugging, and the linker\ncan do much better optimization since it knows which object files are needed\nand which are not."]}),"\n",(0,r.jsx)(n.p,{children:"So for normal development, developers only need to build the Debug version,\nwhich does not require a good network or powerful machine. Though the Release\nversion then requires much better hardware to build, it can generate better\noptimized binaries."}),"\n",(0,r.jsxs)(n.h2,{id:"the-gn-update",children:["The ",(0,r.jsx)(n.code,{children:"gn"})," update"]}),"\n",(0,r.jsx)(n.p,{children:"Being one of the largest projects in the world, most normal systems are not\nsuitable for building Chromium, and the Chromium team develops their own build\ntools."}),"\n",(0,r.jsxs)(n.p,{children:["Earlier versions of Chromium were using ",(0,r.jsx)(n.code,{children:"gyp"})," as a build system, but it suffers\nfrom being slow, and its configuration file becomes hard to understand for complex\nprojects. After years of development, Chromium switched to ",(0,r.jsx)(n.code,{children:"gn"})," as a\nbuild system, which is much faster and has a clear architecture."]}),"\n",(0,r.jsxs)(n.p,{children:["One of the improvements of ",(0,r.jsx)(n.code,{children:"gn"})," is to introduce ",(0,r.jsx)(n.code,{children:"source_set"}),", which represents\na group of object files. In ",(0,r.jsx)(n.code,{children:"gyp"}),", each module was represented by either\n",(0,r.jsx)(n.code,{children:"static_library"})," or ",(0,r.jsx)(n.code,{children:"shared_library"}),", and for the normal build of Chromium,\neach module generated a static library and they were linked together in the\nfinal executable. By using ",(0,r.jsx)(n.code,{children:"gn"}),", each module now only generates a bunch of\nobject files, and the final executable just links all the object files together,\nso the intermediate static library files are no longer generated."]}),"\n",(0,r.jsx)(n.p,{children:"This improvement however made great trouble to libchromiumcontent, because\nthe intermediate static library files were actually needed by libchromiumcontent."}),"\n",(0,r.jsxs)(n.p,{children:["The first try to solve this was to ",(0,r.jsxs)(n.a,{href:"https://github.com/electron/libchromiumcontent/pull/239",children:["patch ",(0,r.jsx)(n.code,{children:"gn"})," to generate static library files"]}),",\nwhich solved the problem, but was far from a decent solution."]}),"\n",(0,r.jsxs)(n.p,{children:["The second try was made by ",(0,r.jsx)(n.a,{href:"https://github.com/alespergl",children:"@alespergl"})," to\n",(0,r.jsx)(n.a,{href:"https://github.com/electron/libchromiumcontent/pull/249",children:"produce custom static libraries from the list of object files"}),".\nIt used a trick to first run a dummy build to collect a list of generated\nobject files, and then actually build the static libraries by feeding\n",(0,r.jsx)(n.code,{children:"gn"})," with the list. It only made minimal changes to Chromium's source\ncode, and kept Electron's building architecture still."]}),"\n",(0,r.jsx)(n.h2,{id:"summary",children:"Summary"}),"\n",(0,r.jsx)(n.p,{children:"As you can see, compared to building Electron as part of Chromium, building\nChromium as a library takes greater efforts and requires continuous\nmaintenance. However the latter removes the requirement of powerful hardware\nto build Electron, thus enabling a much larger range of developers to build and\ncontribute to Electron. The effort is totally worth it."})]})}function c(e={}){let{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(d,{...e})}):d(e)}},28453(e,n,i){i.d(n,{R:()=>s,x:()=>a});var t=i(96540);let r={},o=t.createContext(r);function s(e){let n=t.useContext(o);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:s(e.components),t.createElement(o.Provider,{value:n},e.children)}},50128(e){e.exports=JSON.parse('{"permalink":"/blog/electron-internals-building-chromium-as-a-library","source":"@site/blog/electron-internals-building-chromium-as-a-library.md","title":"Electron Internals: Building Chromium as a Library","de
1scription":"Electron is based on Google\'s open-source Chromium, a project that is not","date":"2017-03-03T00:00:00.000Z","tags":[{"inline":false,"label":"Electron Internals","permalink":"/blog/tags/internals","description":"\'Technical deep dives through Electron\'s source code\'\\n"}],"readingTime":6.47,"hasTruncateMarker":false,"authors":[{"name":"zcbenz","url":"https://github.com/zcbenz","imageURL":"https://github.com/zcbenz.png?size=96","key":"zcbenz","page":null}],"frontMatter":{"title":"Electron Internals: Building Chromium as a Library","date":"2017-03-03T00:00:00.000Z","authors":"zcbenz","slug":"electron-internals-building-chromium-as-a-library","tags":["internals"]},"unlisted":false,"prevItem":{"title":"Project of the Week: Voltra","permalink":"/blog/voltra"},"nextItem":{"title":"Project of the Week: WordPress Desktop","permalink":"/blog/wordpress"}}')}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.