笨辦法學(xué)C 練習(xí)28:Makefile 進(jìn)階

練習(xí)28:Makefile 進(jìn)階

原文:Exercise 28: Intermediate Makefiles

譯者:飛龍

在下面的三個(gè)練習(xí)中你會(huì)創(chuàng)建一個(gè)項(xiàng)目的目錄框架焕妙,用于構(gòu)建之后的C程序。這個(gè)目錄框架會(huì)在這本書中剩余的章節(jié)中使用,并且這個(gè)練習(xí)中我會(huì)涉及到Makefile便于你理解它。

這個(gè)結(jié)構(gòu)的目的是,在不憑借配置工具的情況下隧土,使構(gòu)建中等規(guī)模的程序變得容易。如果完成了它命爬,你會(huì)學(xué)到很多GNU make和一些小型shell腳本方面的東西次洼。

基本的項(xiàng)目結(jié)構(gòu)

首先要做的事情是創(chuàng)建一個(gè)C的目錄狂阿基,并且放置一些多續(xù)項(xiàng)目都擁有的遇骑,基本的文件和目錄卖毁。這是我的目錄:

$ mkdir c-skeleton
$ cd c-skeleton/
$ touch LICENSE README.md Makefile
$ mkdir bin src tests
$ cp dbg.h src/   # this is from Ex20
$ ls -l
total 8
-rw-r--r--  1 zedshaw  staff     0 Mar 31 16:38 LICENSE
-rw-r--r--  1 zedshaw  staff  1168 Apr  1 17:00 Makefile
-rw-r--r--  1 zedshaw  staff     0 Mar 31 16:38 README.md
drwxr-xr-x  2 zedshaw  staff    68 Mar 31 16:38 bin
drwxr-xr-x  2 zedshaw  staff    68 Apr  1 10:07 build
drwxr-xr-x  3 zedshaw  staff   102 Apr  3 16:28 src
drwxr-xr-x  2 zedshaw  staff    68 Mar 31 16:38 tests
$ ls -l src
total 8
-rw-r--r--  1 zedshaw  staff  982 Apr  3 16:28 dbg.h
$

之后你會(huì)看到我執(zhí)行了ls -l票堵,所以你會(huì)看到最終結(jié)果赡盘。

下面是每個(gè)文件所做的事情:

LICENSE

如果你在項(xiàng)目中發(fā)布源碼术羔,你會(huì)希望包含一份協(xié)議虽抄。如果你不這么多靡菇,雖然你有代碼的版權(quán)沃但,但是通常沒有人有權(quán)使用琢歇。

README.md

對(duì)你項(xiàng)目的簡(jiǎn)要說明谆甜。它以.md結(jié)尾媒鼓,所以應(yīng)該作為Markdown來解析届吁。

Makefile

這個(gè)項(xiàng)目的主要構(gòu)建文件。

bin/

放置可運(yùn)行程序的地方绿鸣。這里通常是空的疚沐,Makefile會(huì)在這里生成程序。

build/

當(dāng)值庫和其它構(gòu)建組件的地方潮模。通常也是空的亮蛔,Makefile會(huì)在這里生成這些東西。

src/

放置源碼的地方擎厢,通常是.c.h文件究流。

tests/

放置自動(dòng)化測(cè)試的地方。

src/dbg.h

我將練習(xí)20的dbg.h復(fù)制到了這里动遭。

我剛才分解了這個(gè)項(xiàng)目框架的每個(gè)組件芬探,所以你應(yīng)該明白它們?cè)趺垂ぷ鳌?/p>

Makefile

我要講到的第一件事情就是Makefile,因?yàn)槟憧梢詮闹辛私馄渌鼥|西的情況厘惦。這個(gè)練習(xí)的Makeile比之前更加詳細(xì)偷仿,所以我會(huì)在你輸入它之后做詳細(xì)的分解。

CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)
LIBS=-ldl $(OPTLIBS)
PREFIX?=/usr/local

SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))

TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))

TARGET=build/libYOUR_LIBRARY.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))

# The Target Build
all: $(TARGET) $(SO_TARGET) tests

dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all

$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
       ar rcs $@ $(OBJECTS)
       ranlib $@

$(SO_TARGET): $(TARGET) $(OBJECTS)
       $(CC) -shared -o $@ $(OBJECTS)

build:
       @mkdir -p build
       @mkdir -p bin

# The Unit Tests
.PHONY: tests
tests: CFLAGS += $(TARGET)
tests: $(TESTS)
       sh ./tests/runtests.sh

valgrind:
       VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" $(MAKE)

# The Cleaner
clean:
       rm -rf build $(OBJECTS) $(TESTS)
       rm -f tests/tests.log
       find . -name "*.gc*" -exec rm {} \;
       rm -rf `find . -name "*.dSYM" -print`

# The Install
install: all
       install -d $(DESTDIR)/$(PREFIX)/lib/
       install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/

# The Checker
BADFUNCS='[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)'
check:
       @echo Files with potentially dangerous functions.
       @egrep $(BADFUNCS) $(SOURCES) || true
       

要記住你應(yīng)該使用一致的Tab字符來縮進(jìn)Makefile。你的編輯器應(yīng)該知道怎么做炎疆,但是如果不是這樣你可以換個(gè)編輯器。沒有程序員會(huì)使用一個(gè)連如此簡(jiǎn)單的事情都做不好的編輯器国裳。

頭部

這個(gè)Makefile設(shè)計(jì)用于構(gòu)建一個(gè)庫形入,我們之后會(huì)用到它,并且通過使用GNU make的特殊特性使它在任何平臺(tái)上都可用缝左。我會(huì)在這一節(jié)拆分它的每一部分亿遂,先從頭部開始。

Makefile:1

這是通常的CFLAGS渺杉,幾乎每個(gè)項(xiàng)目都會(huì)設(shè)置蛇数,但是帶有用于構(gòu)建庫的其它東西。你可能需要為不同平臺(tái)調(diào)整它是越。要注意最后的OPTFLAGS變量可以讓使用者按需擴(kuò)展構(gòu)建選項(xiàng)耳舅。

Makefile:2

用于鏈接庫的選項(xiàng),同樣也允許其它人使用OPTFLAGS變量擴(kuò)展鏈接選項(xiàng)倚评。

Makefile:3

設(shè)置一個(gè)叫做PREFIX的可選變量浦徊,它只在沒有PREFIX設(shè)置的平臺(tái)上運(yùn)行Makefile時(shí)有效。這就是?=的作用天梧。

Makefile:5

這神奇的一行通過執(zhí)行wildcard搜索在src/中所有*.c文件來動(dòng)態(tài)創(chuàng)建SOURCES變量盔性。你需要提供src/**/*.csrc/*.c以便GNU make能夠包含src目錄及其子目錄的所有此類文件。

Makefile:6

一旦你創(chuàng)建了源文件列表呢岗,你可以使用patsubst命令獲取*.c文件的SOURCES來創(chuàng)建目標(biāo)文件的新列表冕香。你可以告訴patsubst把所有%.c擴(kuò)展為%.o,并將它們賦給OBJECTS后豫。

Makefile:8

再次使用wildcard來尋找所有用于單元測(cè)試的測(cè)試源文件悉尾。它們存放在不同的目錄中。

Makefile:9

之后使用相同的patsubst技巧來動(dòng)態(tài)獲得所有TEST目標(biāo)挫酿。其中我去掉了.c后綴焕襟,使整個(gè)程序使用相同的名字創(chuàng)建。之前我將.c替換為.o來創(chuàng)建目標(biāo)文件饭豹。

Makefile:11

最后鸵赖,我將最終目標(biāo)設(shè)置為build/libYOUR_LIBRARY.a,你可以為你實(shí)際構(gòu)建的任何庫來修改它拄衰。

這就是Makefile的頭部了它褪,但是我應(yīng)該對(duì)“讓其他人擴(kuò)展構(gòu)建”做個(gè)解釋。你在運(yùn)行它的時(shí)候可以這樣做:

# WARNING! Just a demonstration, won't really work right now.
# this installs the library into /tmp
$ make PREFIX=/tmp install
# this tells it to add pthreads
$ make OPTFLAGS=-pthread

如果你傳入匹配Makefile中相同名稱的變量翘悉,它們會(huì)在構(gòu)建中生效茫打。你可以利用它來修改Makefile的運(yùn)行方式。第一條命令改變了PREFIX,使它安裝到/tmp老赤。第二條設(shè)置了OPTFLAGS轮洋,為之添加了pthread選項(xiàng)。

構(gòu)建目標(biāo)

我會(huì)繼續(xù)Makefile的分解抬旺,這一部分用于構(gòu)建目標(biāo)文件(object file)和目標(biāo)(target):

Makefile:14

要記住在沒有提供目標(biāo)時(shí)make會(huì)默認(rèn)運(yùn)行第一個(gè)目標(biāo)弊予。這里它叫做all:,并且它提供了$(TARGET) tests作為構(gòu)建目標(biāo)开财。查看TARGET變量汉柒,你會(huì)發(fā)現(xiàn)這就是庫文件,所以all:首先會(huì)構(gòu)建出庫文件责鳍。之后碾褂,tests目標(biāo)會(huì)構(gòu)建單元測(cè)試。

Makefile:16

另一個(gè)用于執(zhí)行“開發(fā)者構(gòu)建”的目標(biāo)历葛,它介紹了一種為單一目標(biāo)修改選項(xiàng)的技巧正塌,如果我執(zhí)行“開發(fā)構(gòu)建”,我希望CFLAGS包含類似Wextra這樣用于發(fā)現(xiàn)bug的選項(xiàng)恤溶。如果你將它們放到目標(biāo)的那行中传货,并再編寫一行來指向原始目標(biāo)(這里是all),那么它就會(huì)將改為你設(shè)置的選項(xiàng)宏娄。我通常將它用于在不同的平臺(tái)上設(shè)置所需的不同選項(xiàng)问裕。

Makefile:19

構(gòu)建TARGET庫,然而它同樣使用了15行的技巧孵坚,向一個(gè)目標(biāo)提供選項(xiàng)來為當(dāng)前目標(biāo)修改它們粮宛。這里我通過適用+=語法為庫的構(gòu)建添加了-fPIC

Makefile:20

現(xiàn)在這一真實(shí)目標(biāo)首先創(chuàng)建build目錄卖宠,之后編譯所有OBJECTS巍杈。

Makefile:21

運(yùn)行實(shí)際創(chuàng)建TARGETar的命令。$@ $(OBJECTS)語法的意思是扛伍,將當(dāng)前目標(biāo)的名稱放在這里筷畦,并把OBJECTS的內(nèi)容放在后面。這里$@的值為19行的$(TARGET)刺洒,它實(shí)際上為build/libYOUR_LIBRARY.a鳖宾。看起來在這一重定向中它做了很多跟蹤工作逆航,它也有這個(gè)功能鼎文,并且你可以通過修改頂部的TARGET,來構(gòu)建一個(gè)全新的庫因俐。

Makefile:22

最后拇惋,在TARGET上運(yùn)行ranlib來構(gòu)建這個(gè)庫周偎。

Makefile:24-24

用于在build/bin/目錄不存在的條件下創(chuàng)建它們。之后它被19行引用撑帖,那里提供了build目標(biāo)來確保build/目錄已創(chuàng)建蓉坎。

你現(xiàn)在擁有了用于構(gòu)建軟件的所需的所有東西。之后我們會(huì)創(chuàng)建用于構(gòu)建和運(yùn)行單元測(cè)試的東西胡嘿,來執(zhí)行自動(dòng)化測(cè)試蛉艾。

單元測(cè)試

C不同于其他語言,因?yàn)樗子跒槊總€(gè)需要測(cè)試的東西創(chuàng)建小型程序灶平。一些測(cè)試框架試圖模擬其他語言中的模塊概念伺通,并且執(zhí)行動(dòng)態(tài)加載箍土,但是它在C中并不適用逢享。這也不是必要的,因?yàn)槟憧梢詢H僅編寫一個(gè)程序用于每個(gè)測(cè)試吴藻。

我接下來會(huì)涉及到Makefile的這一部分瞒爬,并且你會(huì)看到test/目錄中真正起作用的內(nèi)容。

Makefile:29

如果你擁有一個(gè)不是“真實(shí)”的目標(biāo)沟堡,只有有個(gè)目錄或者文件叫這個(gè)名字侧但,你需要使用g.PHONY:標(biāo)簽來標(biāo)記它,以便make忽略該文件航罗。

Makefile:30

我使用了與修改CFLAGS變量相同的技巧禀横,并且將TARGET添加到構(gòu)建中,于是每個(gè)測(cè)試程序都會(huì)鏈接TARGET庫粥血。這里它會(huì)添加build/libYOUR_LIBRARY.a用于鏈接柏锄。

Makefile:31

之后我創(chuàng)建了實(shí)際的test:目錄,它依賴于所有在TESTS變量中列出的程序复亏。這一行實(shí)際上說趾娃,“Make,請(qǐng)使用你已知的程序構(gòu)建方法缔御,以及當(dāng)前CFLAGS設(shè)置的內(nèi)容來構(gòu)建TESTS中的每個(gè)程序抬闷。”

Makefile:32

最后耕突,所有TESTS構(gòu)建完之后笤成,會(huì)運(yùn)行一個(gè)我稍后創(chuàng)建的簡(jiǎn)單shell腳本,它知道如何全部運(yùn)行他們并報(bào)告它們的輸出眷茁、這一行實(shí)際上運(yùn)行它來讓你看到測(cè)試結(jié)果疹启。

Makefile:34-35

為了能夠動(dòng)態(tài)使用Valgrind重復(fù)運(yùn)行測(cè)試,我創(chuàng)建了valgrind:標(biāo)簽蔼卡,它設(shè)置了正確的變量并且再次運(yùn)行它喊崖。它會(huì)將Valgrind的日志放到/tmp/valgrind-*.log挣磨,你可以查看并了解發(fā)生了什么。之后tests/runtests.sh看到VALGRIND變量時(shí)荤懂,它會(huì)明白要在Valgrind下運(yùn)行測(cè)試程序茁裙。

你需要為單元測(cè)試創(chuàng)建一個(gè)小型的shell腳本,它知道如何運(yùn)行程序节仿。我們開始創(chuàng)建這個(gè)tests/runtests.sh腳本:

echo "Running unit tests:"

for i in tests/*_tests
do
    if test -f $i
    then
        if $VALGRIND ./$i 2>> tests/tests.log
        then
            echo $i PASS
        else
            echo "ERROR in test $i: here's tests/tests.log"
            echo "------"
            tail tests/tests.log
            exit 1
        fi
    fi
done

echo ""

當(dāng)我提到單元測(cè)試如何工作時(shí)晤锥,我會(huì)在之后用到它。

清理工具

我已經(jīng)有了用于單元測(cè)試的工具廊宪,所以下一步就是創(chuàng)建需要重置時(shí)的清理工具矾瘾。

Makefile:38

clean:目標(biāo)在我需要清理這個(gè)項(xiàng)目的任何時(shí)候都會(huì)執(zhí)行清理。

Makefile:39-42

這會(huì)清理不同編譯器和工具留下的多數(shù)垃圾箭启。它也會(huì)移除build/目錄并且使用了一個(gè)技巧來清理XCode為調(diào)試目的而留下的*.dSYM壕翩。

如果你碰到了想要執(zhí)行清理的垃圾,你只需要簡(jiǎn)單地?cái)U(kuò)展需要?jiǎng)h除的文件列表傅寡。

安裝

然后放妈,我會(huì)需要一種安裝項(xiàng)目的方法,對(duì)Makefile來說就是把構(gòu)建出來的庫放到通常的PREFIX目錄下荐操,它通常是/usr/local/lib芜抒。

Makefile:45

它會(huì)使install:依賴于all:目錄,所以當(dāng)你運(yùn)行make install之后也會(huì)先確保一切都已構(gòu)建托启。

Makefile:46

接下來我使用install程序來創(chuàng)建lib目標(biāo)的目錄宅倒。其中我通過使用兩個(gè)為安裝者提供便利的變量,嘗試讓安裝盡可能靈活屯耸。DESTDIR交給安裝者拐迁,便于在安全或者特定的目錄里執(zhí)行自己的構(gòu)建。PREFIX在別人想要將項(xiàng)目安裝到其它目錄而不是/user/local時(shí)會(huì)被使用肩民。

Makefile:47

在此之后我使用insyall來實(shí)際安裝這個(gè)庫唠亚,到它需要安裝的地方。

install程序的目的是確保這些事情都設(shè)置了正確的權(quán)限持痰。當(dāng)你運(yùn)行make install時(shí)你通常使用root權(quán)限來執(zhí)行灶搜,所以通常的構(gòu)建過程應(yīng)為make && sudo make install

檢查工具

Makefile的最后一部分是個(gè)額外的部分工窍,我把它包含在我的C項(xiàng)目中用于發(fā)現(xiàn)任何使用C中“危險(xiǎn)”函數(shù)的情況割卖。這些函數(shù)是字符串函數(shù)和另一些“不保護(hù)棧”的函數(shù)患雏。

Makefile:50

設(shè)置變量鹏溯,它是個(gè)稍大的正則表達(dá)式,用于檢索類似strcpy的危險(xiǎn)函數(shù)淹仑。

Makefile:51

這是check:目標(biāo)丙挽,使你能夠隨時(shí)執(zhí)行檢查肺孵。

Makefile:52

它只是一個(gè)打印信息的方式,使用了@echo來告訴make不要打印命令颜阐,只需打印輸出平窘。

Makefile:53

對(duì)源文件運(yùn)行egrep命令來尋找任何危險(xiǎn)的字符串。最后的|| true是一種方法凳怨,用于防止make認(rèn)為egrep沒有找到任何東西是執(zhí)行失敗瑰艘。

當(dāng)你執(zhí)行它之后,它會(huì)表現(xiàn)得十分奇怪肤舞,如果沒有任何危險(xiǎn)的函數(shù)紫新,你會(huì)得到一個(gè)錯(cuò)誤。

你會(huì)看到什么

我在完成這個(gè)項(xiàng)目框架目錄的構(gòu)建之前李剖,還設(shè)置了兩個(gè)額外的練習(xí)芒率。下面這是我對(duì)Makefile特性的測(cè)試結(jié)果:

$ make clean
rm -rf build  
rm -f tests/tests.log 
find . -name "*.gc*" -exec rm {} \;
rm -rf `find . -name "*.dSYM" -print`
$ make check
Files with potentially dangerous functions.
^Cmake: *** [check] Interrupt: 2

$ make
ar rcs build/libYOUR_LIBRARY.a 
ar: no archive members specified
usage:  ar -d [-TLsv] archive file ...
      ar -m [-TLsv] archive file ...
      ar -m [-abiTLsv] position archive file ...
      ar -p [-TLsv] archive [file ...]
      ar -q [-cTLsv] archive file ...
      ar -r [-cuTLsv] archive file ...
      ar -r [-abciuTLsv] position archive file ...
      ar -t [-TLsv] archive [file ...]
      ar -x [-ouTLsv] archive [file ...]
make: *** [build/libYOUR_LIBRARY.a] Error 1
$ make valgrind
VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" make
ar rcs build/libYOUR_LIBRARY.a 
ar: no archive members specified
usage:  ar -d [-TLsv] archive file ...
      ar -m [-TLsv] archive file ...
      ar -m [-abiTLsv] position archive file ...
      ar -p [-TLsv] archive [file ...]
      ar -q [-cTLsv] archive file ...
      ar -r [-cuTLsv] archive file ...
      ar -r [-abciuTLsv] position archive file ...
      ar -t [-TLsv] archive [file ...]
      ar -x [-ouTLsv] archive [file ...]
make[1]: *** [build/libYOUR_LIBRARY.a] Error 1
make: *** [valgrind] Error 2
$

當(dāng)我運(yùn)行clean:目標(biāo)時(shí)它會(huì)生效,但是由于我在src/目錄中并沒有任何源文件杖爽,其它命令并沒有真正起作用敲董。我會(huì)在下個(gè)練習(xí)中補(bǔ)完它紫皇。

附加題

  • 嘗試通過將源文件和頭文件添加進(jìn)src/慰安,來使Makefile真正起作用,并且構(gòu)建出庫文件聪铺。在源文件中不應(yīng)該需要main函數(shù)化焕。
  • 研究check:目標(biāo)會(huì)使用BADFUNCS的正則表達(dá)式來尋找什么函數(shù)。
  • 如果你沒有做過自動(dòng)化測(cè)試铃剔,查詢有關(guān)資料為以后做準(zhǔn)備撒桨。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市键兜,隨后出現(xiàn)的幾起案子凤类,更是在濱河造成了極大的恐慌,老刑警劉巖普气,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谜疤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡现诀,警方通過查閱死者的電腦和手機(jī)夷磕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仔沿,“玉大人坐桩,你說我怎么就攤上這事》怙保” “怎么了绵跷?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵膘螟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我碾局,道長(zhǎng)萍鲸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任擦俐,我火速辦了婚禮脊阴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蚯瞧。我一直安慰自己嘿期,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布埋合。 她就那樣靜靜地躺著备徐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甚颂。 梳的紋絲不亂的頭發(fā)上蜜猾,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音振诬,去河邊找鬼蹭睡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛赶么,可吹牛的內(nèi)容都是我干的肩豁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼辫呻,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼清钥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起放闺,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤祟昭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后怖侦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體篡悟,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年础钠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恰力。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旗吁,死狀恐怖踩萎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情很钓,我是刑警寧澤香府,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布董栽,位于F島的核電站,受9級(jí)特大地震影響企孩,放射性物質(zhì)發(fā)生泄漏锭碳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一勿璃、第九天 我趴在偏房一處隱蔽的房頂上張望擒抛。 院中可真熱鬧,春花似錦补疑、人聲如沸歧沪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诊胞。三九已至,卻和暖如春锹杈,著一層夾襖步出監(jiān)牢的瞬間撵孤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工竭望, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留邪码,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓市框,卻偏偏與公主長(zhǎng)得像霞扬,于是被迫代替她去往敵國(guó)和親糕韧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子枫振,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)萤彩,斷路器粪滤,智...
    卡卡羅2017閱讀 134,651評(píng)論 18 139
  • 練習(xí)26:編寫第一個(gè)真正的程序 原文:Exercise 26: Write A First Real Progra...
    布客飛龍閱讀 905評(píng)論 2 37
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,803評(píng)論 6 342
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,074評(píng)論 25 707
  • 想說都是雜言 粗略看到網(wǎng)傳演員QRL突然死亡 網(wǎng)傳 以為絕對(duì)是謠傳 沒想到點(diǎn)進(jìn)去后已經(jīng)確認(rèn)是事實(shí) 印象中此人該是陽...
    大風(fēng)小雨閱讀 278評(píng)論 0 0