Article 5 proved the tree builds and boots. Now we trace what actually made that happen: envsetup shell glue, lunch target discovery, inherited product makefiles, Soong namespaces, and the output metadata files that explain where the build really went.
In the last article, we finally got a successful build and a booting emulator.
That was the fun milestone. But it also hides a lot.
We typed . build/envsetup.sh, lunch, and mka, and the tree eventually handed us system.img, vendor.img, super.img, and a booting emulator. If you stop there, Android still feels a bit like ritual. Type the right words, wait a long time, hope the tree is in a forgiving mood.
Article 6 is where we slow that down.
This is not going to be a full Soong internals lecture. That would be too much, too early, and honestly not very useful yet. What we do need is a practical map of the build path we already used: where the lunch target comes from, which product files it inherits, why the output directory ended up as emu64x, and which build artifacts are worth reading after the build finishes.
Start with the shell glue
The first thing to get straight is this: envsetup.sh is not the build system.
It is the shell glue around the build system.
That sounds small, but it matters. When you source:
. build/envsetup.sh
you are not compiling anything yet. You are loading helper functions and environment behavior into the current shell. That is why commands like lunch, m, and mka suddenly exist. It is also why the emulator later becomes easy to launch from the same shell.
In this tree, build/envsetup.sh also contains the flow that Article 5 used without spelling it out:
lunch()parses the target you asked for_lunch_meatvalidates itbuild_build_var_cacheresolves build variablesset_stuff_for_environmentexports target-aware environment valuesprintconfigprints the summary we used as our checkpoint
If you grep build/envsetup.sh for those function names, the shell-side build path becomes pretty readable very quickly. You can literally see the main entry points sitting there in the file.
That is already a more useful mental model than “source the file because tutorials say so.”
The important practical point is this: envsetup.sh gives the shell its build vocabulary, and lunch turns that vocabulary into a specific product configuration.
Pick the target up from the right place
In Article 5, we used:
lunch lineage_sdk_phone_x86_64-trunk_staging-userdebug
Now the obvious question is: where does lineage_sdk_phone_x86_64 actually come from?
For this tree, the first useful answer lives here:
vendor/lineage/build/target/product/AndroidProducts.mk
That file is one of the build system’s product index points. It lists the product makefiles LineageOS exposes and the lunch choices associated with them.
In our run, the first grep already gave us two useful breadcrumbs:
- the
lineage_sdk_phone_x86_64.mkproduct file entry - the
lineage_sdk_phone_x86_64-userdebuglunch choice entry
For our emulator target, the important line is the makefile entry for:
vendor/lineage/build/target/product/lineage_sdk_phone_x86_64.mk
That is the first real breadcrumb.
It means the lunch target is not magic text. It resolves to an actual product makefile under vendor/lineage/build/target/product/.
Follow the product inheritance chain
Once you open vendor/lineage/build/target/product/lineage_sdk_phone_x86_64.mk, the build starts looking less mysterious.
That file does not define everything itself. It inherits other product files:
device/generic/goldfish/64bitonly/product/sdk_phone64_x86_64.mkvendor/lineage/build/target/product/lineage_generic_target.mkdevice/generic/goldfish/board/kernel/x86_64.mk
This is the part a lot of beginners miss.
The final build target is usually not one giant file. It is a stack of inherited product fragments. Each one contributes something different:
- base emulator product behavior
- LineageOS-specific product wrapping
- kernel selection
- package lists
- copy files
- board details
For this specific build, the most important inherited file is:
device/generic/goldfish/64bitonly/product/sdk_phone64_x86_64.mk
That is where the emu64x identity becomes explicit:
PRODUCT_NAME := sdk_phone64_x86_64PRODUCT_DEVICE := emu64x
Those lines do more to explain the output path from Article 5 than a page of abstract build-system talk.
So now the odd thing from Article 5 stops looking odd.
We lunched lineage_sdk_phone_x86_64, but the output directory landed under:
out/target/product/emu64x
That happened because the LineageOS wrapper product inherits a goldfish product definition whose device name is emu64x.
That is not a naming accident. That is the inheritance chain doing exactly what it is supposed to do.
emu64x.
Where Soong enters the picture
This is usually where people get fuzzy and start treating “Soong” as a word that just means “Android build stuff.”
Let’s keep it more practical than that.
The product makefiles decide what kind of thing we are building. They describe product identity, inheritance, copy files, package selection, partition sizing, board details, and the namespaces the build should expose.
Soong is the module build system that takes that configured world and turns module definitions into real build actions.
There is more machinery under it, especially Kati and Ninja, but we do not need to unpack all of that in one article. For now, the useful thing is to understand that Soong is not guessing in isolation. It is working from the target shape that lunch and the product files helped define.
That is why the lunch summary in Article 5 was useful. It showed values like:
TARGET_PRODUCT=lineage_sdk_phone_x86_64PRODUCT_SOONG_NAMESPACES=device/generic/goldfish hardware/google/camera/devices/EmulatedCamera
Those namespaces are not random decoration. They tell you which module definition trees are being exported into the Soong world for this build. In this emulator product chain, you can see those namespace additions in goldfish product files like device/generic/goldfish/product/generic.mk.
So the relationship is not:
- makefiles on one side
- Soong on another side
- and nobody talking to each other
It is more like this:
- product files shape the target
- build variables capture that target state
- Soong sees the relevant namespaces and module world for that state
- the build then materializes into
out/target/product/<device>
That is enough understanding for this article. We do not need to dive into Blueprint syntax yet just to be useful.
Read the breadcrumbs in out/target/product
After the build succeeds, most people only look for the flashy files:
system.imgvendor.imgsuper.imgvbmeta.img
Those matter, yes. But Article 6 is a good place to notice the quieter files too.
Under:
out/target/product/emu64x
the build also left some very useful breadcrumbs:
module-info.jsoninstalled-files.txtmisc_info.txtbuild_fingerprint-lineage_sdk_phone_x86_64.txt
These are the kinds of files that help you reason about the build instead of just admiring the output images.
module-info.json is a module inventory. It is huge, but useful when you want to know what the build knows how to produce.
installed-files.txt is one of the cleanest “what actually landed?” reports in the tree. It shows installed artifacts and sizes. If you want to understand why an image got big, this is one of the places to start.
misc_info.txt is full of packaging and partition metadata. It is not pretty, but it tells you real things about how the target was assembled.
And the fingerprint file is a nice sanity breadcrumb tying the built output back to the product identity you selected.
Even the first few lines are useful. In this run, misc_info.txt immediately showed AVB and image-packaging settings, while installed-files.txt started with framework jars, APEX packages, native libraries, and binaries. That is already enough to make the output directory feel readable instead of opaque.
This is why this article fits well right after the first build. Once you know these files exist, the tree stops feeling like a black box. You have places to look when something gets weird later.
What this article should leave you with
If Article 5 proved that the tree builds, this article should leave you with a better question: what exactly told it how to build?
By this point, you should be able to say all of this without hand-waving:
envsetup.shprepares the shell and build helperslunchresolves a real product target, not a magical stringlineage_sdk_phone_x86_64comes from LineageOS product makefiles- that product inherits goldfish emulator definitions
- the inherited device identity is why output lands under
emu64x - Soong is seeing a target-shaped world, not building in a vacuum
- the output directory contains metadata breadcrumbs, not just flashable images
That is enough understanding for now.
We do not need to become build-system archaeologists before changing ArjunaOS. But we do need enough of the mental map that later edits feel intentional.
Coming up next
In the next article, we finally start changing what the system looks like.
The build and boot path is proven now. The build system is less mysterious. That means we can move into rebranding without doing it blindly.
Article 7 is where LineageOS finally starts turning into ArjunaOS.