Android編譯系統(tǒng)簡單總結

寫在開頭:本文為Android10.0編譯系統(tǒng)這個系列的筆記。
要站在巨人的肩膀上去學習,知識就在那里斋日,一個好的老師會讓你學得更快林艘,更好。我剛入門那會兒宪塔,上司教導我磁奖,不會沒有關系,要多借鑒他人的經驗某筐,見得多了比搭,自己多做總結就好了,記下來的就是自己的了南誊。大部分人的知識是都不是憑空而來的身诺,創(chuàng)新的畢竟是少數(shù)。
筆記的作用就是回憶的時候抄囚,找重點霉赡。每個人掌握的知識不一樣,關注點也不同幔托。記錄穴亏,可以讓下次快速找到某個知識點。畢竟搜索有時候并不可靠,由于信息有丟失嗓化,所以我都會注明來源锅劝。
本篇目錄結構如下:

  • 1.概述
  • 2.編譯構成
  • 3.編譯步驟
  • 4.main.mk
  • 5.工具鏈的關系
  • 6.system.img的打包
  • 7.Kati
  • 8.Blueprint
  • 9.Ninja
  • 10.android.mk的總結

1.概述

image.png

Ninja

Ninja是一個致力于速度的小型編譯系統(tǒng)(類似于Make),如果把其他編譯系統(tǒng)比做高級語言的話蟆湖,Ninja就是匯編語言故爵。通常使用Kati或soong把makefile轉換成Ninja files,然后用Ninja編譯隅津。
Ninja的誕生诬垂,主要是為了提升編譯速度,Ninja中去除了變量的計算伦仍,沒有默認規(guī)則结窘, 依賴必須顯式聲明,從而提升編譯速度充蓝∷矸悖基本上可以認為ninja就是make的最最精簡版。

Android.bp

Android.bp只是一個純粹的配置文件谓苟,不包括分支官脓、循環(huán)語句等控制流程,本質上就是一個json配置文件涝焙。Android.bp 通過Blueprint+soong轉換成ninja的構建規(guī)則文件build.ninja卑笨,再使用ninja來進行構建工作。
Android10.0上仑撞,mk和bp編譯的列表可以從 \out.module_paths中的Android.bp.list赤兴、Android.mk.list中看到,Android10.0還有400多個mk文件沒有被替換完隧哮,Google任重道遠桶良。

Android編譯演進過程:

Android7.0之前 使用GNU Make
Android7.0 引入ninja、kati、Android.bp和soong構建系統(tǒng)
Android8.0 默認打開Android.bp
Android9.0 強制使用Android.bp


https://blog.csdn.net/yiranfeng/article/details/109082489

2.編譯構成

Android的編譯目錄在/build 中,看一下Android 10源碼中的build目錄哼审,現(xiàn)在是這個樣子:


image.png

這個目錄中可以看到core文件夾被link到了make/core魔吐,envsetup.sh被link到make/envsetup.sh,這主要是為了對使用者屏蔽切換編譯系統(tǒng)的差異。
這里重點看四個文件夾:blueprint、kati、make瑰步、soong


image.png

Soong編譯系統(tǒng)家族成員及各自關系

在編譯過程中,Android.bp會被收集到out/soong/build.ninja.d,blueprint以此為基礎璧眠,生成out/soong/build.ninja
Android.mk會由kati/ckati生成為out/build-aosp_arm.ninja
兩個ninja文件會被整合進入out/combined-aosp_arm.ninja

3.編譯步驟

編譯會用到3條命令缩焦。

source build/envsetup.sh
lunch aosp_arm-eng // 或者 m PRODUCT-aosp_x86_64-eng 读虏,Android10.0不一定需要lunch命令
make -j8      //編譯模塊也可以直接用 m libart

-j8代表用系統(tǒng)的8個線程去編譯,因為一般不是單人使用所以不能太高袁滥,會影響其他人的使用盖桥。

image.png

加載envsetup.sh题翻,初始化一下環(huán)境變量揩徊。
envsetup.sh 主要做了下面幾個事情:

image.png

環(huán)境變量初始化完成后嵌赠,我們需要選擇一個編譯目標塑荒。lunch 主要作用是根據用戶輸入或者選擇的產品名來設置與具體產品相關的環(huán)境變量。eng表示工程版本词渤,一般調試編譯用userdebug把篓,正式版本用user口芍。
區(qū)別可看這篇: Android編譯選項eng、user裤翩、userdebug的區(qū)別

執(zhí)行完lunch命令后,就可以使用make命令來執(zhí)行編譯Build烫堤。
main.mk文件把一些環(huán)境變量和目標都配置好后荣赶,會執(zhí)行envsetup.sh中的make()進行編譯


soong的編譯過程

4.main.mk

執(zhí)行runKatiBuild時,有個重要的步驟鸽斟,就是加載build/make/core/main.mk拔创,main.mk文件是Android Build系統(tǒng)的主控文件。從main.mk開始富蓄,將通過include命令將其所有需要的.mk文件包含進來剩燥,最終在內存中形成一個包括所有編譯腳本的集合,這個相當于一個巨大Makefile文件立倍。Makefile文件看上去很龐大灭红,其實主要由三種內容構成: 變量定義、函數(shù)定義和目標依賴規(guī)則口注,此外mk文件之間的包含也很重要变擒。


image.png

main.mk主要做了以下幾件事情:

1.定義編譯目標product
2.加載config.mk來初始化相關變量,檢測編譯環(huán)境和目標環(huán)境
3.清除規(guī)則寝志,清除out目錄中的dex文件
4.加載build/croe/definitions.mk,定義了很多通用函數(shù)娇斑,供編譯過程調用
5.加載平臺開發(fā)工具包 build/make/core/pdk_config.mk
6.加載系統(tǒng)中所有的Android.mk,最終會被存放到out/.module_paths/Android.mk.list
7.Link 類型檢查,校驗Link
8.要為此產品生成的模塊的基本列表由相應的產品定義文件指定材部,這些定義在"product_config.mk"中
9.運行時APEX庫毫缆,并進行檢查校驗
10.將所有要安裝的模塊都保存在變量ALL_DEFAULT_INSTALLED_MODULES中,并且將build/core/Makefie文件加載進來乐导。build/core/Makefie文件會根據要安裝的模塊生成system.img苦丁、super.img、boot.img和recovery.img等鏡像文件的生成規(guī)則
11.定義編譯的image目標
12.構建文件兽叮,然后將其打包成rom格式

5.工具鏈的關系

Android10.0的編譯系統(tǒng)中芬骄,涉及以下一些工具鏈猾愿,由這些工具鏈相輔相成,才最終編譯出了我們所需要的鏡像版本账阻。
Android10.0編譯工具鏈:

soong kati blueprint ninja 

Soong

Soong 構建系統(tǒng)是在 Android 7.0 (Nougat) 中引入的蒂秘,旨在取代 Make。它利用 Kati GNU Make 克隆工具和 Ninja 構建系統(tǒng)組件來加速 Android 的構建淘太。
Soong是由Go語言寫的一個項目姻僧,從Android 7.0開始,在prebuilts/go/目錄下新增了Go語言所需的運行環(huán)境蒲牧,Soong在編譯時使用撇贺,解析Android.bp,將之轉化為Ninja文件冰抢,完成Android的選擇編譯松嘶,解析配置工作等。故Soong相當于Makefile編譯系統(tǒng)的核心挎扰,即build/make/core下面的內容翠订。
另外Soong還會編譯產生一個androidmk命令,可以用來手動將Android.mk轉換成Android.bp文件遵倦。不過這只對無選擇尽超、循環(huán)等復雜流程控制的Android.mk生效。
soong腳本和代碼目錄:/build/soong

kati

kati是一個基于Makefile來生成ninja.build的小項目梧躺。主要用于把Makefiel轉成成ninja file似谁,自身沒有編譯能力,轉換后使用Ninja編譯掠哥。
在編譯過程中巩踏,kati負責把既有的Makefile、Android.mk文件续搀,轉換成Ninja文件蛀缝。在Android 8.0以后,它與Soong一起目代,成為Ninja文件的兩大來源。Kati更像是Google過渡使用的一個工具嗤练,等所有Android.mk都被替換成Android.bp之后榛了,Kati有可能退出Android編譯過程.
在單獨使用時,它對普通的小項目還能勉強生效煞抬。面對復雜的霜大、多嵌套的Makefile時,它往往無法支持革答,會出現(xiàn)各種各樣的問題战坤。當然曙强,也可以理解為,它只為Android而設計途茫。
kati腳本和代碼目錄:/build/kati

Blueprint

Blueprint是生成碟嘴、解析Android.bp的工具,是Soong的一部分囊卜。Soong則是專為Android編譯而設計的工具娜扇,Blueprint只是解析文件的形式,而Soong則解釋內容的含義栅组。

ninja

最開始雀瓢,Ninja 是用于Chromium 瀏覽器中,Android 在SDK 7.0 中也引入了Ninja玉掸。
Ninja是一個致力于速度的小型編譯系統(tǒng)(類似于Make)刃麸,如果把其他編譯系統(tǒng)比做高級語言的話,Ninja就是匯編語言司浪。通常使用Kati或soong把makefile轉換成Ninja files泊业,然后用Ninja編譯。
主要兩個特點:
1)可以通過其他高級的編譯系統(tǒng)生成其輸入文件断傲;
2)它的設計就是為了更快的編譯脱吱;
ninja核心是由C/C++編寫的,同時有一部分輔助功能由python和shell實現(xiàn)认罩。由于其開源性箱蝠,所以可以利用ninja的開源代碼進行各種個性化的編譯定制。
從Android 7開始垦垂,編譯時默認使用Ninja宦搬。但是,Android項目里是沒有.ninja文件的劫拗。遵循Ninja的設計哲學间校,編譯時,會先把Makefile通過kati轉換成.ninja文件页慷,然后使用ninja命令進行編譯憔足。

Android.mk文件、Android.bp酒繁、kati滓彰、Soong、Blueprint州袒、Ninja之間的關系如下:

Android.bp --> Blueprint --> Soong --> Ninja 
Makefile or Android.mk --> kati --> Ninja 
(Android.mk --> Soong --> Blueprint --> Android.bp)

Blueprint是生成揭绑、解析Android.bp的工具,是Soong的一部分郎哭。Soong則是專為Android編譯而設計的工具他匪,Blueprint只是解析文件的形式菇存,而Soong則解釋內容的含義。
Android.mk可以通過Soong提供的androidmk轉換成Android.bp邦蜜,但僅限簡單配置依鸥。目前Oreo的編譯流程中,仍然是使用kati來做的轉換畦徘。
現(xiàn)存的Android.mk文件毕籽、既有的Android.bp,都會分別被轉換成Ninja井辆。
從Android.mk與其它Makefile关筒,會生成out/build-<product_name>.ninja文件。而從Android.bp杯缺,則會生成out/soong/build.ninja蒸播。此外,還會生成一個較小的out/combined-<product_name>.ninja文件萍肆,負責把二者組合起來袍榆,作為執(zhí)行入口。

最終塘揣,Ninja文件才是真正直接控制源碼編譯的工具包雀。

6.system.img的打包

main.mk中,最后兩步定義了需要編譯的image和構建一個rom的過程亲铡。
main.mk中只是做了一些定義和啟動編譯流程才写,正在的image打包在build/core/Makefile中完成。

在Makefile中奖蔓,.PHONY后面的target表示的也是一個偽造的target, 而不是真實存在的文件target赞草,注意Makefile的target默認是文件。如果有人依賴它吆鹤,就無條件執(zhí)行厨疙。

[build/core/Makefile]
...
.PHONY: systemimage
...
.PHONY: systemimage-nodeps snod
...

# Rules that need to be present for the all targets, even
# if they don't do anything.
.PHONY: systemimage
systemimage:
 
...
INSTALLED_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/system.img
SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)
 
$(INSTALLED_SYSTEMIMAGE_TARGET): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH)
       @echo "Install system fs image: $@"
       $(copy-file-to-target)
       $(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))
 
systemimage: $(INSTALLED_SYSTEMIMAGE_TARGET)
 
.PHONY: systemimage-nodeps snod
systemimage-nodeps snod: $(filter-out systemimage-nodeps snod,$(MAKECMDGOALS)) \
                   | $(INTERNAL_USERIMAGES_DEPS)
       @echo "make $@: ignoring dependencies"
       $(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE_TARGET))
       $(hide) $(call assert-max-image-size,$(INSTALLED_SYSTEMIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))
...

關于system.img,這里定義了兩個偽目標systemimage 和 systemimage-nodeps疑务。

systemimage 依賴于INSTALLED_SYSTEMIMAGE_TARGET沾凄,最終生成目標文件

$(PRODUCT_OUT)/system.img
$(INSTALLED_SYSTEMIMAGE_TARGET): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH)
       @echo "Install system fs image: $@"
       $(copy-file-to-target)
       $(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))

INSTALLED_SYSTEMIMAGE_TARGET依賴于BUILT_SYSTEMIMAGE和RECOVERY_FROM_BOOT_PATCH,再調用了函數(shù)copy-file-to-target進行文件拷貝
最后調用build/make/tools/releasetools/build_image.py來生成system.img知允。
img格式是一種文件壓縮格式搭独。img就是把一些文件壓縮打包。

INTERNAL_USERIMAGES_DEPS:列出了制作system.img所需要的工具廊镜,例如out/host/linux-x86/bin/simg2img、out/host/linux-x86/bin/mkuserimg_mke2fs 等唉俗,如果支持f2fs的文件系統(tǒng)嗤朴,會加載out/host/linux-x86/bin/make_f2fs
INTERNAL_SYSTEMIMAGE_FILES:列出了制作system.img所需要的文件配椭,釋義如下:
ALL_GENERATED_SOURCES:描述的是要拷貝到目標設備上去的由工具自動生成的源代碼文件。
ALL_DEFAULT_INSTALLED_MODULES:描述的是所有需要安裝的module
PDK_FUSION_SYSIMG_FILES:是從PDK(Platform Development Kit)提取出來的相關文件
RECOVERY_RESOURCE_ZIP:描述的是Android的recovery系統(tǒng)要使用的資源文件雹姊,對應于/system/etc目錄下的recovery-resource.dat文件股缸。
PDK_FUSION_SYMLINK_STAMP:PDK的符號鏈接文件

7.Kati

Kati詳解-Android10.0編譯系統(tǒng)(五)
這篇文字不多,而且都是理論性的吱雏,因此不做提取敦姻。

8.Blueprint

Blueprint簡介-Android10.0編譯系統(tǒng)(六)

Blueprint

是一個meta-build系統(tǒng),它讀取描述需要構建的模塊的bp文件歧杏,并生成Ninja描述需要運行的命令及其依賴項的清單镰惦。

bluepring--soong的編譯過程經歷下面四個階段:

1.運行microfactory.bash以建立minibp -
2.運行.minibootstrap / build.ninja來構建.bootstrap / build.ninja -
3.運行.bootstrap / build.ninja來構建和運行主構建器 -
4.運行build.ninja來構建您的代碼

Blueprint文件是一個偽python數(shù)據格式的模塊列表,其中模塊類型看起來像函數(shù)調用犬绒,模塊的屬性看起來像可選參數(shù)旺入。例如,一個簡單的模塊可能看起來像:

 cc_library {
      name: "cmd",
      srcs: [
          "main.c",
      ],
      deps: [
          "libc",
      ],
  }
 
  subdirs = ["subdir1", "subdir2"]

格式說明:

[module type] {
    name: "[name value]",
    [property1 name]:"[property1 value]",
    [property2 name]:"[property2 value]",
}

bp文件中的模塊(module) 以模塊類型(module type)開頭凯力,后面跟著一系列的屬性(property)茵瘾。每個模塊都必須具有一個屬性名為name的屬性,并且name的屬性值在所有Android.bp文件中必須是唯一的咐鹤。

更多編寫規(guī)則拗秘,可參考 :
Android 編譯之android.bp
Android.bp 語法淺析-Android10.0編譯系統(tǒng)(八)

基本上寫的時候,就是參照著源碼去寫祈惶。


常用模塊類型

Android系統(tǒng)中現(xiàn)在基本是bp和mk文件混著用雕旨。bp一般要搭配go腳本來達到宏控制的目的。Android.bp 文件很簡單行瑞。它們不包含任何條件語句奸腺,也不包含控制流語句;所有復雜問題都由用 Go 編寫的構建邏輯處理血久。
具體可參考這篇:
Android.bp正確姿勢添加宏控制編譯指南
這個博主寫的系列文章也都很不錯突照,細節(jié)性很強。

//xxxparser.go
package  xxxparser

import (
        "android/soong/android"
        "android/soong/cc"
)

func init() {
    // resister a module "xxxparser_defaults"
    android.RegisterModuleType("xxxparser_defaults", xxxdroidDefaultsFactory)
}

func xxxdroidDefaultsFactory() (android.Module) {
    module := cc.DefaultsFactory()
    android.AddLoadHook(module, xxxdroidDefaults)
    return module
}

func xxxdroidDefaults(ctx android.LoadHookContext) {
   type props struct {
        Cflags []string
    }   
    p := &props{}
    p.Cflags = globalDefaults(ctx)
    ctx.AppendProperties(p) 
}

func globalDefaults(ctx android.BaseContext) ([]string) {
    var cppflags []string
    if ctx.AConfig().Getenv("ANDROIDBP_FUN") == "YES" {
          cppflags = append(cppflags,"-DXXX")
    }   
    return cppflags
}


在Android.bp開頭位置引入go腳本文件xxxparser.go氧吐,如下:

//引入go腳本
bootstrap_go_package {
    name: "soong-xxxparser",
    pkgPath: "android/soong/xxxparser",
    deps: [
    "blueprint",
    "blueprint-pathtools",
    "soong",
    "soong-android",
    "soong-cc",
    "soong-genrule",
    ],  
    srcs: [
        "xxxparser.go",
    ],  
    pluginFor: ["soong_build"],
}

xxxparser_defaults {
    name: "xxxparser_defaults",
}

ANDROIDBP_FUN = ["YES"]
cc_binary {
    defaults: ["xxxparser_defaults"],
    name: "AndroidBp",
    srcs: ["main.c"],
    
   
    cflags: ["-Wno-error=implicit-function-declaration"],



    shared_libs: [
        "libcutils",
        "liblog",
        "libutils",
    ],

}

以上語句可以保證運行Android.bp時讹蘑,先編譯對應的xxxparser.go運行go腳本時,會首先運行init函數(shù)筑舅,將 xxxdroidDefaultsFactory函數(shù)注冊到module中座慰,之后調用xxxdroidDefaultsFactory函數(shù)時,會將回調函數(shù) xxxdroidDefaults注冊進去之后調用 privateParserDefaults 時翠拣,我們可以從 ctx.AConfig() 中獲取好多屬性
(參考 build/soong/android/config.go 中對 build/soong/android/module.go中的 androidBaseContext interface的各種函數(shù)實現(xiàn))版仔,其中有一項是獲取宏值的,之后回調xxxdroidDefaults添加宏信息。

9.Ninja

Ninja簡介-Android10.0編譯系統(tǒng)(九)
Ninja提升編譯速度的方法-Android10.0編譯系統(tǒng)(十)

Ninja

是一個編譯框架蛮粮,會根據相應的ninja格式的配置文件進行編譯益缎,但是ninja文件一般不會手動修改,而是通過將Android.bp文件轉換成ninja格文件來編譯然想。
Ninja是一個注重速度的小型構建系統(tǒng)莺奔。它與其他構建系統(tǒng)在兩個主要方面不同:它被設計為使其輸入文件由更高級別的構建系統(tǒng)生成,并且被設計為盡可能快地運行構建变泄。

編譯分析
從Android O開始令哟,soong已經是google的入口。從soong入口后妨蛹,會經soong_ui,soong,kati,blueprint幾個階段屏富,把mk,bp轉換成ninja文件后滑燃,然后執(zhí)行ninja命令解析ninja文件進行編譯役听。
如下圖所示整個編譯過程,準備過程非常冗長表窘。

image.png

每次編譯都要重新收集所有的文件典予、.mk、.bp的修改乐严,然后重新生成build.ninja瘤袖,在合并成combined-aosp_arm.ninja。
大部分情況下昂验,研發(fā)的工作是不斷的修改.c .h .cpp .java 然后增量捂敌,此時真正的編譯工作是非常少的,這樣相對而言既琴,準備工作往往是占大頭的占婉,所以我們可以考慮舍棄combined-aosp_arm.ninja之前的準備過程。
這里以增量編譯init_system為例甫恩,之前我們已經編好了init_system逆济,然后如果我們繼續(xù)用m命令單編init_system,需要2分鐘磺箕。

ingresge:~/AP/AOSP_Q$ time m init_system
[100% 6336/6336] Install: out/target/product/generic/fake_packages/init_system-timestamp
 
#### build completed successfully (02:37 (mm:ss)) ####
 
real    2m36.672s
user    43m51.510s
sys     2m51.991s

為了對比編譯時間奖慌,我們直接拋棄了編譯的環(huán)境和ninja文件生成的逐步過程,我們使用下面的命令直接跑ninja松靡,結果只花了5秒简僧。

命令:
time prebuilts/build-tools/linux-x86/bin/ninja -v -d keepdepfile init_system -f out/combined-aosp_arm.ninja -w dupbuild=err
編譯結果:
ingresge:~/AP/AOSP_Q$ time prebuilts/build-tools/linux-x86/bin/ninja -v -d keepdepfile init_system -f out/combined-aosp_arm.ninja -w dupbuild=err
[7/7] /bin/bash -c "(rm -f out/target/product/generic/system/bin/init ) && (cp out/target/product/generic/obj/EXECUTABLES/init_second_stage_intermediates/init out/target/product/generic/system/bin/init )"
 
real    0m5.351s
user    0m14.752s
sys     0m3.201s

這種命令可以寫到sh腳本中去,方便快速執(zhí)行。
在修改build/make/envsetup.sh雕欺,新增一個qninja函數(shù)岛马。

function qninja()
{
    local cmdline="time prebuilts/build-tools/linux-x86/bin/ninja -v -d keepdepfile $@ -f out/combined-aosp_arm.ninja -w dupbuild=warn"
    echo $cmdline
    $cmdline
}

只是修改了某個模塊中的.c .h .cpp .java后棉姐,進行增量,編譯命令如下:

source build/envsetup.sh
qninja init_system

加載配置文件蛛枚,執(zhí)行qninja函數(shù)谅海。

早期可以是mm單編某個應用提升編譯效率,現(xiàn)在就可以使用Ninja了蹦浦。

編譯Settings
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-xxx.ninja Settings -j32
編譯selinux
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-xxx.ninja selinux_policy -j32
編譯Framework
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-xxx.ninja framework -j32
全編譯
./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-xxx.ninja -j32 2>&1 |tee ninja_build.log
 

10.android.mk的總結

android.mk的使用實例介紹看這篇。
Android 編譯之android.mk
文章中介紹了以下幾種mk的編寫撞蜂。

image.png

參考鏈接:
編譯系統(tǒng)入門篇-Android10.0編譯系統(tǒng)(一)
Android編譯系統(tǒng)簡要介紹和學習計劃
從CM刷機過程和原理分析Android系統(tǒng)結構
Android編譯系統(tǒng)分析五:system.img的生成過程
Makefile中.PHONY的作用
Android系統(tǒng)快速編譯方式ninja
Makefile概念入門
Android 編譯之android.mk

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末盲镶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蝌诡,更是在濱河造成了極大的恐慌溉贿,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浦旱,死亡現(xiàn)場離奇詭異宇色,居然都是意外死亡,警方通過查閱死者的電腦和手機颁湖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門宣蠕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人甥捺,你說我怎么就攤上這事抢蚀。” “怎么了镰禾?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵皿曲,是天一觀的道長。 經常有香客問我吴侦,道長屋休,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任备韧,我火速辦了婚禮劫樟,結果婚禮上,老公的妹妹穿的比我還像新娘盯蝴。我一直安慰自己毅哗,他們只是感情好,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布捧挺。 她就那樣靜靜地躺著虑绵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪闽烙。 梳的紋絲不亂的頭發(fā)上翅睛,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天声搁,我揣著相機與錄音,去河邊找鬼捕发。 笑死疏旨,一個胖子當著我的面吹牛,可吹牛的內容都是我干的扎酷。 我是一名探鬼主播檐涝,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼法挨!你這毒婦竟也來了谁榜?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤凡纳,失蹤者是張志新(化名)和其女友劉穎窃植,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荐糜,經...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡巷怜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了暴氏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片延塑。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖偏序,靈堂內的尸體忽然破棺而出页畦,到底是詐尸還是另有隱情,我是刑警寧澤研儒,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布豫缨,位于F島的核電站,受9級特大地震影響端朵,放射性物質發(fā)生泄漏好芭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一冲呢、第九天 我趴在偏房一處隱蔽的房頂上張望舍败。 院中可真熱鬧,春花似錦敬拓、人聲如沸邻薯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厕诡。三九已至,卻和暖如春营勤,著一層夾襖步出監(jiān)牢的瞬間灵嫌,已是汗流浹背壹罚。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留寿羞,地道東北人猖凛。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像绪穆,于是被迫代替她去往敵國和親辨泳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內容

  • Android編譯流程 一玖院、編譯流程 google已經給出了android的原生編譯流程:source build...
    Chan_98af閱讀 4,071評論 0 0
  • 如果有自己編譯過AOSP的源碼漠吻,可能大家都會遇到這樣的一個問題: 在剛剛開始接觸AOSP的時候,我基本上都會直奔主...
    minhelloworld閱讀 3,499評論 0 0
  • 引言 Android編譯知識的梳理文章共三篇: Android 編譯之make基礎[https://www.jia...
    qiuxintai閱讀 37,944評論 2 17
  • 一簡介 之前有android blueprint分析司恳,soong只不過對blueprint進行了擴展,可以識別各種...
    Little熊貓閱讀 8,290評論 0 4
  • 介紹Android最新的編譯系統(tǒng) 一绍傲、簡介 早期的Android系統(tǒng)都是采用Android.mk的配置來編譯源碼扔傅,...
    momxmo閱讀 1,136評論 0 1