精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

GNU從頭構建系統實踐

系統 Linux
本篇從我的實踐總結的角度,并闡述如何從頭開始規劃一個基于GNU構建系統的項目。事實上,隨著開發者對跨平臺認知的深入和完善,才能逐漸掌握GNU構建。注意:本文的例子不依賴于任何IDE和編輯器。這樣讀者可以從根本上認識到每個文件的作用。

[[172534]]

在上一篇概念:GNU構建系統和Autotool,我對GNU構建系統從用戶視角和開發者視角分別進行了闡述。本篇從我的實踐總結的角度,并闡述如何從頭開始規劃一個基于GNU構建系統的項目。事實上,隨著開發者對跨平臺認知的深入和完善,才能逐漸掌握GNU構建。注意:本文的例子不依賴于任何IDE和編輯器。這樣讀者可以從根本上認識到每個文件的作用。

安裝autotools

需要安裝的工具包括autoconf、automake、libtool。

目錄結構規劃

首先,我們需要規劃項目的目錄結構。假設,我們的項目叫gnu-build。設想如下目錄結構:

  1. gnu-build 
  2.  
  3. |---build(用于編譯) 
  4.  
  5. |---src 
  6.  
  7. |---common 
  8.  
  9. |---Makefile.am 
  10.  
  11. |---pool.c 
  12.  
  13. |---alloc.c 
  14.  
  15. |---list.c 
  16.  
  17. |... 
  18.  
  19. |---core 
  20.  
  21. |---Makefile.am 
  22.  
  23. |---main.c 
  24.  
  25. |... 
  26.  
  27. |---test 
  28.  
  29. |---Makefile.am 
  30.  
  31. |---test.c 
  32.  
  33. |... 
  34.  
  35. |---Makefile.am 
  36.  
  37. |---configure.ac 
  38.  
  39. |---Makefile.am 
  40.  
  41. |---.gitignore  

從上面的目錄結構可以看出:

  1. 根目錄有一個configure.ac,這是構建系統的核心文件之一,描述整個構建的依賴和輸出,是configure腳本的原型。
  2. 每個目錄(包括根目錄)都有一個Makefile.am,這些文件是生成Makefile的主要來源。使用Makefile.am的優點是可以結合configure.ac、比手動編寫Makefile方便很多。
  3. 在src目錄下放置源代碼,源代碼被分成common、core、test。common用來實現一些可重用的代碼,比如通用數據結構,內存管理,異常的封裝;core用來放置直接編譯成可執行程序的代碼,比如main.c等;test用于編寫單元測試程序。
  4. build目錄用于存放編譯過程中的臨時文件和編譯得到了目標文件。一般我們總是cd在build目錄中,并執行../configure來configure,并在build目錄下make。這樣的話,由configure產生的文件不會污染源碼空間。我們需要做的只是在.gitignore中添加build/。

在使用autoreconf的過程中,還將在各個目錄下生成其他的文件(尤其是根目錄)。現在我們只需要創建上述必要文件。

configure.ac可以通過在根目錄下執行autoscan程序生成。如果你已經有一些代碼了,使用autoscan生成configure.ac是個不錯的開始。

configure.ac的基本編寫

通用宏

每個configure.ac都需要如下兩行。分別說明需要的autoconf的最低版本,以及程序的包名、版本、bug反饋郵件地址。

  1. AC_PREREQ(2.59) 
  2.  
  3. AC_INIT([gnu-build], [1.0], [support@gnubuild.org]) 

configure.ac通篇幾乎都是采用這種類似函數調用的語法編寫,這些稱為宏的語句,會被autoconf工具識別,并展開成相應的shell腳本,最終成為configure腳本。除此之外,也可以混合地直接編寫shell腳本。autoconf預置了很多實用的宏,可以減少工作量,后面你將看到宏的價值。

可以直接編寫shell腳本,但是推薦盡量使用宏。因為shell程序有很多種(sh,bash,ksh,csh...),想要寫出可移植的shell并不是件容易的事情。

接著,通常使用AC_CONFIG_SRCDIR來定位一個源代碼文件,如此一來,autoconf程序會檢查該文件是否存在,以確保autoconf的工作目錄的正確性。這里,我們指向src/core/main.c。

  1. AC_CONFIG_SRCDIR([src/core/main.c]) 

定義輸出的宏

一般來說,都會編寫一個header輸出定義。這是我們用到的第一個輸出指令。輸出指令告訴configure,需要生成哪些文件。AC_CONFIG_HEADERS的含義是在指定的目錄生成.h,一般叫做config.h,你也可以指定其他名字。

  1. AC_CONFIG_HEADERS([src/common/config.h]) 

那么這個config.h究竟有什么用呢?回憶一下,configure程序的主要目的是檢測目標平臺的軟硬件環境,從而在實際調用make命令編譯程序前,對編譯工作進行一個預先的配置,這里的配置落實到底,主要就是生成Makefile和config.h:

  1. Makefile.am --> Makefile.in --> Makefile 
  2.                              | 
  3.                            configure* 
  4.                              | 
  5.                 config.h.in --> config.h  

那么我們的程序必需要通過某種方式,得知環境的不同,從而通過預編譯做出響應。這里的響應主要分兩塊:

  1. 對于源代碼而言,通過config.h中的宏定義,來改變編譯行為。
  2. 對于Makefile.am而言,通過configure.ac導出的變量,來動態改變Makefile。

在后面的敘述中,可以通過代碼體會這兩點。所以這里,為了讓我們的源碼有能力根據環境來改變編譯行為,生成config.h通常是必要的。

另一個輸出宏是AC_CONFIG_FILES,針對這個例子,告訴autoconf,我們需要輸出Makefile文件:

  1. AC_CONFIG_FILES([Makefile 
  2.                  src/Makefile 
  3.                  src/core/Makefile 
  4.                  src/common/Makefile 
  5.                  src/test/Makefile 
  6.                  ]) 
  7. AC_OUTPUT  

注意到每個目錄都需要由對應的Makefile文件,這是automake多目錄組織Makefile的通用做法。后面會講到如何編寫各個目錄下的Makefile.am。

AC_CONFIG_FILES一般跟AC_OUTPUT一起寫在configure.ac的最后部分。

automake聲明

為了配合automake,需要用AM_INIT_AUTOMAKE初始化automake:

  1. AM_INIT_AUTOMAKE([foreign]) 

這里foreign是個可選項,設置foreign跟調用automake --foreign是等價的,前一篇有講到。

libtool聲明

配合使用libtool,需要加入LT_INIT,這樣autoreconf會自動調用libtoolize

  1. LT_INIT 

編譯器檢查

configure可以幫助我們檢查編譯和安裝過程中需要的系統工具是否存在。一般在進行其他檢查前,先做此類檢查。例如下面是一些常用的檢查:

  1. # 聲明語言為C 
  2. AC_LANG(C) 
  3.  
  4. # 檢查cc 
  5. AC_PROG_CC 
  6.  
  7. # 檢查預編譯器 
  8. AC_PROG_CXX 
  9.  
  10. # 檢查ranlib 
  11. AC_PROG_RANLIB 
  12.  
  13. # 檢查lex程序,gnu下通常叫flex 
  14. AC_PROG_LEX 
  15.  
  16. # 檢查yacc,gnu下通常叫bison 
  17. AC_PROG_YACC 
  18.  
  19. # 檢查sed 
  20. AC_PROG_SED 
  21.  
  22. # 檢查install程序 
  23. AC_PROG_INSTALL 
  24.  
  25. # 檢查ln -s 
  26. AC_PROG_LN_S  

針對這個例子我們只需要檢查cc,cxx就可以了。

Makefile.am的基本編寫

Makefile.am文件是一種更高層次的Makefile,抽象程度更高,比Makefile更容易編寫,除了兼容Makefile語法外,通常只需包含一些變量定義即可。automake程序負責解析,并生成Makefile.in,而Makefile.in從表現上與Makefile已經十分接近,只差變量替換了。configure腳本執行后,Makefile.in將最終轉變成Makefile。

子目錄引用

在本例中每個目錄下都有Makefile.am。根目錄的Makefile.am生成的Makefile將是make程序的默認入口,但是根目錄實際上并不包含任何需要構建的文件。對于需要引用子目錄的Makefile來構建的時候,使用SUBDIRS羅列包含其他Makefile.am的子目錄。因此,對于根目錄的Makefile.am只需要寫一行:

  1. SUBDIRS = src 

同理,src目錄下的Makefile.am只需要

  1. SUBDIRS = common src test 

定義目標

對于包含有源代碼文件的目錄。首先,我們需要定義編譯的目標,目標可能是庫文件或可執行文件,目標又分為需要安裝和不需要安裝兩種。例如對于common目錄

下的源代碼,我們希望生成一個不需要安裝的庫文件(使用libtool),因為這個庫文件只在本項目內使用,那么common/Makefile.am應當這樣寫:

  1. noinst_LTLIBRARIES = libcommon.la 
  2.  
  3. libcommon_la_SOURCES = pool.c alloc.c list.c  

定義了一個目標libcommon.la。由于使用libtool,所以庫文件必須以lib開頭,后綴為.la。

目標的基本格式為where_PRIMARY = targets ... where表示安裝位置,可選擇bin、lib、noinst、check(make check時構建),還可以自定義。我們著重討論前三種:

  • bin:表示安裝到bindir目錄下,這種情況下會編譯出動態庫
  • lib:表示安裝到libdir目錄下,這種情況下會編譯出動態庫
  • noinst:表示不安裝,這種情況下會編譯出靜態庫,在其他目標引用該目標時將進行靜態鏈接

PRIMARY可以是PROGRAMS LIBRARIES LTLIBRARIES HEADERS SCRIPTS DATA。著重討論前三種:

  • PROGRAMS:表示目標是可執行文件
  • LIBRARIES:表示目標是庫文件,通過后綴來區別靜態庫或動態庫
  • LTLIBRARIES:表示是libtool庫文件,統一后綴為.la

與Makefile的思想一樣,目標的生成需要定義來源,通常目標是有一些源程序文件得到的。Makefile.am中只需定義xxx_SOURCES,后面跟隨構建xxx這個目標需要的源代碼文件列表即可。注意到xxx是目標的名字,并且.字符需要使用_代替。

定義編譯選項

core目錄下需要生成可執行目標,但是在鏈接時,需要用到libcommon.la,此時core/Makefile.am可以寫成

  1. bin_PROGRAMS = gnu-build 
  2.  
  3. GNU_BUILD_SOURCES = main.c 
  4.  
  5. GNU_BUILD_LIBADD = $(top_builddir)/src/common/libcommon.la  

這里多了一行GNU_BUILD_LIBADD,target_LIBADD的形式表示為target添加庫文件的引用,這種引用是靜態的還是動態的取決于引用的庫文件是否支持動態庫,如果支持動態庫,libtool優先采用動態鏈接。而由于libcommon.la指定為noinst,所以不可能以動態鏈接的形式存在,這里必然是靜態鏈接。

$(top_builddir)引用的是make發生時的工作目錄,上文提到,我們將在build目錄下進行構建,那么庫文件會生成在build目錄下,而不是源碼根目錄下,所以$(top_builddir)實際就是gnu-build/build目錄,而這樣可以很好的支持在另一個目錄中編譯程序。與之相對應的是$(top_srcdir)對應的是源碼的根目錄,即gnu-build目錄。

還有多個可以配置用于改變編譯和鏈接選項的配置項:

  • xxx_LDADD:為鏈接器增加參數,一般用于第三方庫的引用。比如-L -l
  • xxx_LIBADD:聲明庫文件引用,一般對于本項目中的庫文件引用采用這種形式。
  • xxx_LDFLAG:鏈接器選項
  • xxx_CFLAGS:c編譯選項,如-D -I
  • xxx_CPPFLAGS:預編譯選項
  • xxx_CXXFLAGS: c++編譯選項

如果xxx是AM,則表示全局target都采用這個選項。

安裝路徑

剛剛提到的bindir和libdir是configure目錄體系下的,類似的路徑還有:

  1. prefix                /usr/local 
  2. exec-prefix            {prefix} 
  3. bindir                {exec-prefix}/bin 
  4. libdir                {exec-prefix}/lib 
  5. includedir            {prefix}/include 
  6. datarootdir            {prefix}/share 
  7. datadir             {datarootdir} 
  8. mandir                {datarootdir}/man 
  9. infodir                {datarootdir}/info 
  10. ...  

可以看到prefix在這里的地位是一個頂層的路徑,其他的路徑直接或間接與之有關。而prefix的默認值為/usr/local。所以可執行程序默認總是安裝在/usr/local/bin。用戶總是可以在調用configure腳本時通過--prefix指定prefix。更詳細的路徑列表可以通過./configure --help了解。

開始構建

填充一些源代碼后,就可以使用autoreconf了,只需要在根目錄下執行autoreconf --install即可。

  1. [root@xxx gnu-build]# autoreconf --install 

前一篇中,對autoreconf的整個過程和產生的文件做了詳盡的分析和闡述,讀者也應該十分清楚這里將得到若干Makefile.in和common/config.h.in文件。

如果這個過程順利的話,就可以在build目錄下構建了:

  1. # cd build 
  2.  
  3. # ../configure 
  4.  
  5. # make  

這里configure后,會在build目錄下生成對應位置的Makefile和common/config.h文件,而不是生成在源碼目錄中從而污染源碼

至此,你已經完成了一個項目的基本構建框架,后面的事情,就是逐步完善構建對環境的依賴。

在configure.ac中配置環境檢查

autoconf為程序員提供的最為重要的功能就是提供了一種便捷、穩定、可移植的方式,讓程序能在特定目標平臺和目標環境上安全的編譯運行程序。不過,autoconf只是提供了一些宏,用來簡化環境檢查。而究竟要檢查些什么,如何合理的利用這些宏完成目的,依舊是需要大量的積累的。筆者在這里對一些常用的宏進行一些介紹。

可執行文件檢查

有些第三方庫在安裝到系統后,會附帶安裝若干可執行程序,并可在環境變量的支持下直接運行。有時,我們通過檢查此類可執行程序是否存在,來初步判斷該第三方庫是否已經安裝在目標平臺。其中一種常用的宏是AC_CHECK_PROGS

  1. # 聲明一個變量PERL,檢查perl程序是否存在并可執行 
  2. # 如果不存在$PERL變量將是NOTFOUND,如果存在$PERL變量將是perl 
  3. AC_CHECK_PROGS([PERL], [perl], [NOTFOUND]) 
  4.  
  5. # 聲明一個變量TAR,檢查tar和gtar程序是否存在并可執行 
  6. # 如果不存在$TAR變量將是:,如果存在,第一個可用的程序名將賦值給$TAR 
  7. AC_CHECK_PROGS([TAR], [tar gtar], [:])  

GNU軟件有一種利用pkg-config,來進行自描述的機制。即可以通過注冊軟件自身(通常提供庫文件的軟件),讓pkg-config能夠返回庫文件的安裝路徑等信息,以便以一種統一的方式提供給調用程序。有些庫軟件附帶有獨立的config程序,比如pcre-config和apr-1-config。如果對這類庫提供軟件需要檢查依賴和編譯鏈接,通常可以通過AC_CHECK_PROGS來檢查config程序,從而得到編譯鏈接選項。

打印消息宏

打印消息可以作為調試手段,同時也可以在用戶在configure過程中,給予提示信息。

  1. # error將終止configure 
  2. AC_MSG_ERROR([zlib is required]) 
  3.  
  4. # warn不會終止configure 
  5. AC_MSG_WARN([zlib is not found, xxx will not be support.])  

注意到AC_MSG_ERROR將中斷configure的執行,一般用于必需的編譯環境無法滿足時。

庫檢查宏

檢查某庫是否存在是最重要的功能,因為我們程序往往需要這些庫,甚至是庫中的某個函數的支持才能正確的運行。

使用AC_CHECK_LIB檢查庫以及其中的函數是否存在,該宏的原型為: 

  1. AC_CHECK_LIB (library, function, [action-if-found],[action-if-not-found], [other-libraries]) 
  • library:需要檢查的庫名,無需lib前綴,比如為了檢查libssl是否存在,這里需要傳入ssl
  • function:這個庫中的某個函數名
  • action-if-found:如果找到執行某個動作,這個動作可以是另一個宏,可以是shell腳本。如果不指定這個參數,默認在LIBS環境變量中增加-l選項,從而將在鏈接過程中將這個庫鏈接進來。比如-lssl。并且在config.h中定義一個宏HAVE_LIBlibrary,例如HAVE_LIBSSL。我們的代碼可以根據這個宏得知當前編譯環境是否提供libssl。
  • action-if-not-found:如果找不到則執行某個動作

通過下面幾個宏可以檢查系統是否包含某些頭文件,以及是否支持某些函數:

  • AC_CHECK_FUNCS:檢查是否支持某些函數。作為檢查的副作用,在config.h中會定義一個宏HAVE_funcs(全大寫)
  • AC_CHECK_HEADERS:檢查是否支持某些頭文件。作為檢查的副作用,在config.h中會定義一個宏HAVE_header_H(全大寫)

來舉個例子,大家知道libiconv是一個可以在不同字符集間進行轉化的庫,如果我們的程序希望能夠在不同字符集間轉化的字符串的話,可以使用該庫。然而,在不同平臺上,該庫的移植方式有些區別。

gnu的標準c庫(glibc)在很早的時候就把libiconv集成到了glibc中,因此在linux上可以無需額外的庫支持即可使用iconv。然而,在非linux上,很可能需要額外的libiconv庫。那么如果在非linux的平臺上編寫可移植的程序,可以參考如下的宏組合: 

  1. AC_CHECK_FUNCS(iconv_open, HAVE_ICONV=yes, []) 
  2. if test "x$HAVE_ICONV" = "xyes"then 
  3.      AC_CHECK_HEADERS(langinfo.h, [], AC_MSG_WARN([langinfo.h not found])) 
  4.      AC_CHECK_FUNCS([nl_langinfo], [], [AC_MSG_WARN([nl_langinfo not found])]) 
  5. else 
  6.     AC_CHECK_LIB([iconv], [libiconv_open], [HAVE_ICONV=yes], [AC_MSG_WARN([no iconv found, will not build xm_charconv])]) 
  7.     if test "x$HAVE_ICONV" = "xyes"then 
  8.         LIBICONV="-liconv" 
  9.         SAVED_LIBS=$LIBS 
  10.         LIBS="$LIBS $LIBICONV" 
  11.         AC_CHECK_HEADERS(langinfo.h,  
  12.                      AC_CHECK_FUNCS([nl_langinfo], [], [AC_MSG_ERROR([nl_langinfo not found in your libiconv])]),  
  13.                      AC_CHECK_FUNCS([locale_charset], [], [AC_MSG_ERROR([no langinfo.h nor locale_charset found in libiconv])])) 
  14.         LIBS=$SAVED_LIBS 
  15.     fi 
  16. fi  

在這個例子中,我們可以看到許多技巧。我們來逐一解讀一下:

  1. 首先通過AC_CHECK_FUNCS檢查iconv_open函數,如果在Linux平臺上,通常該函數可以在沒有任何額外庫的情況下提供,所以HAVE_ICONV這個臨時變量將設置為yes。
  2. 接著通過shell的if測試判斷臨時變量HAVE_ICONV是否為yes。
  3. 如果已經檢測到iconv,那么進一步檢查langinfo.h頭文件和nl_langinfo函數,無論是否能檢查通過,由于使用了AC_MSG_WARN,所以configure并不會失敗退出,最多只是提示用戶警告。更重要的是,我們可以通過config.h中的宏,在代碼中得知是否支持頭文件和函數,從而調整編譯分支。具體的在這個例子中這兩個宏分別為HAVE_LANGINFO_H和HAVE_NL_LANGINFO。
  4. 在非linux下可能需要額外的libiconv庫,所以在else分支中,立刻采用AC_CHECK_LIB檢測iconv庫,以及其中的libiconv_open函數。同樣的,如果存在,HAVE_ICONV這個臨時變量將設置為yes。
  5. 在接下來的if測試中,使用到了$LIBS變量,這是一個由編譯器支持的變量,表示在鏈接階段的額外庫參數。當我們檢測到libiconv后,就給這個變量臨時地添加-liconv。這樣接下來的AC_CHECK_FUNCS時,可以利用$LIBS在額外的庫中查找函數。
  6. 檢查langinfo.h頭文件,如果存在則再檢查nl_langinfo函數;如果不存在,則檢查locale_charset函數。從邏輯上看,要么langinfo.h和nl_langinfo同時存在,要么有locale_charset函數,否則就終止configure。
  7. 最后重置$LIBS變量。

變量導出

configure腳本的檢測結果應當有兩個主要出口,一是config.h,它幫助我們在源碼中創建編譯分支;二是Makefile.am,我們可以在Makefile.am中基于這些導出的變量,改變構建方式。

有些宏可以自動幫我們導出到config.h,關于這一點上文已經有所闡述了。而希望導出到Makefile.am則需要我們自己手動調用相關宏。這里主要有兩個宏:

  • AC_SUBST:將一個臨時變量,導出到Makefile.am。實際是在Makefile.in中聲明一個變量,并且在生成Makefile時,由configure腳本對變量的值進行替換。
  • AM_CONDITIONAL:由automake引入,可進行一個條件測試,從而決定是否導出變量。

例如,針對上面iconv的例子,我們有個臨時變量HAVE_ICONV,如果iconv在當前平臺可用,此時HAVE_ICONV將會是yes。所以可以使用AM_CONDITIONAL導出變量:

  1. AM_CONDITIONAL([HAVE_ICONV], [test x$HAVE_ICONV != x]) 

或者無論如何都導出HAVE_ICONV

  1. AC_SUBST(HAVE_ICONV) 

在Makefile.am中,我們可以對變量進行引用,這樣xm_charconv.la就將在HAVE_ICONV導出的情況下構建:

  1. if HAVE_ICONV 
  2.   xm_charconv_LTLIBRARIES = xm_charconv.la 
  3.   ... 
  4. endif  

提供額外用戶參數支持

很多軟件都支持用戶在configure階段,可通過--with-xxx --enable-xxx等命令行選項對軟件進行模塊配置或編譯配置。以--with-xxx為例,我們需要AC_ARG_WITH宏:

  1. AC_ARG_WITH(configfile, 
  2.   [  --with-configfile=FILE   default config file to use], 
  3.   [ ZZ_CONFIGFILE="$withval"], 
  4.   [ ZZ_CONFIGFILE="${sysconfdir}/zz.conf"
  5.   ) 
  6.  
  7. AC_SUBST(ZZ_CONFIGFILE)  

FILE定義該參數的值應當是一個文件路徑(DIR要求一個目錄路徑),該宏需要提供一個默認值,這個例子中是${sysconfdir}/zz.conf,${sysconfdir}引用了${prefix}/etc,而$withval從命令行中引用--with-configfile的值。

最后我們通過AC_SUBST導出一個臨時變量。

上一節提到,導出的臨時變量可以在Makefile.am中引用,所以我們可以在Makefile.am中通過-D傳遞給代碼,從而在代碼中通過宏來引用:

  1. CFLAGS += -DCONFIGFILE=\"$(ZZ_CONFIGFILE)\" 

總結

本文以一個例子,一步步使用GNU構建系統來創建一個項目,并介紹了一些常用的檢測宏。事實上,autotool還有很多宏,甚至可以自定義宏。能否合理利用autotool取決于程序員對可移植性這個問題的經驗和理解。

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2016-09-28 21:50:29

GNUAutotoolLinux

2024-03-01 13:49:00

數據訓練

2023-04-14 11:04:43

2023-06-12 15:43:44

鴻蒙智能家居開發

2023-03-09 15:15:21

鴻蒙模塊編譯

2024-02-21 14:07:00

2016-09-20 13:02:12

CLinuxAutotool

2013-04-10 10:59:45

Linux系統監控collectl

2021-05-24 15:48:38

高德打車系統可觀測性

2024-06-13 08:36:11

2022-05-07 19:51:22

微軟WindowsWindows 12

2023-08-11 17:30:54

決策樹機器學習算法

2019-04-04 09:19:08

日志京東流式計算

2024-08-15 14:48:57

2012-09-29 10:09:19

網站架構后臺構建架構

2019-12-16 12:11:53

Docker容器Kubernetes

2022-11-14 10:49:33

Linux發行版

2024-09-26 16:51:23

2018-11-16 11:54:37

2009-12-18 09:48:26

Linux中應用
點贊
收藏

51CTO技術棧公眾號

538国产精品视频一区二区| 日韩女优电影在线观看| 亚洲天堂电影网| 国产免费黄色网址| 亚洲精品欧美| 一本色道久久综合亚洲精品小说| 亚洲va综合va国产va中文| 日韩经典av| 国产偷国产偷亚洲高清人白洁| 成人欧美在线观看| 日日夜夜综合网| 国产一区二区区别| 日韩欧美视频一区| 一区二区xxx| 高清电影在线免费观看| 国产女同互慰高潮91漫画| αv一区二区三区| 少妇又紧又色又爽又刺激视频| 中文字幕人成人乱码| 亚洲毛片在线看| 国产精品成人免费一区久久羞羞| 人人鲁人人莫人人爱精品| 一区二区三区四区不卡在线| 日韩av在线一区二区三区| 丁香六月色婷婷| 久久99久国产精品黄毛片色诱| 欧美亚洲成人精品| 国产性70yerg老太| 忘忧草精品久久久久久久高清| 日韩av最新在线| 妖精视频在线观看| 欧洲美女精品免费观看视频| 色综合久久中文字幕| av在线播放天堂| a级毛片免费观看在线| 国产日产欧美一区| 蜜桃精品久久久久久久免费影院 | 成人区精品一区二区不卡| 久久久久国产免费免费 | 亚洲欧美偷拍另类| 久九九久频精品短视频| 精品女厕一区二区三区| 久久久久99精品成人片| 午夜伦理在线视频| 亚洲精品免费在线观看| 国产精品久久成人免费观看| 日本成人网址| 中文字幕在线观看一区二区| 婷婷久久伊人| 在线看的av网站| 中文字幕第一区二区| 日韩欧美三级电影| 成人性爱视频在线观看| 国产欧美一区二区三区鸳鸯浴| 欧美伦理一区二区| 免费人成在线观看网站| 国产三级精品视频| 午夜精品电影在线观看| 永久免费在线观看视频| 中文字幕一区二区三区在线观看| 亚洲一卡二卡| av免费网站在线观看| 亚洲综合在线五月| 日本国产在线播放| 中文日产幕无线码一区二区| 欧美在线免费观看亚洲| 奇米影音第四色| av在线播放一区二区| 制服丝袜激情欧洲亚洲| 国产ts在线观看| 女同久久另类99精品国产| 精品视频在线播放免| 免费视频91蜜桃| 久久高清精品| 欧美激情在线观看| 精品国产xxx| 麻豆成人综合网| 91九色视频在线观看| 四虎免费在线观看| 欧美国产1区2区| 国产精品一二三在线观看| 操人在线观看| 欧美日韩一级二级| 动漫美女无遮挡免费| 一道本一区二区三区| 日韩在线观看免费全| 免费无码毛片一区二区app| 亚洲专区一区| 国产综合色香蕉精品| 开心激情综合网| 欧美激情一区二区三区四区| 最近免费观看高清韩国日本大全| 国产精品25p| 欧美日韩久久一区二区| 国产高清成人久久| 精品久久中文| 久久久久久成人精品| 最近中文字幕在线观看| 国产激情精品久久久第一区二区| 久久伦理网站| 国产黄网站在线观看| 欧美日韩午夜视频在线观看| 国产原创精品在线| 另类春色校园亚洲| 最新中文字幕亚洲| 中文字幕一区二区三区精品| 蜜桃视频第一区免费观看| 国产精品日韩一区二区三区| 国产黄在线观看免费观看不卡| 亚洲人成网站精品片在线观看| 无码人妻h动漫| 中文字幕日韩高清在线| 日韩网站在线观看| 国产精品久久久久久久久久久久久久久久久 | 日韩激情综合网| 99riav1国产精品视频| 国产在线观看精品| 国产91免费看| 亚洲欧美日韩国产手机在线 | 精品呦交小u女在线| 中文字幕av播放| 日韩国产欧美视频| 久久精彩视频| 美女精品视频| 8x福利精品第一导航| 精品少妇人妻一区二区黑料社区| 欧美日韩国产成人精品| 成人国产精品免费视频| 毛片在线免费| 精品久久久免费| 亚洲精品久久一区二区三区777 | 久久成人av网站| 一本久道久久综合无码中文| 国产日产亚洲精品系列| 国产亚洲天堂网| 日韩福利视频一区| 欧美精品久久久久久久久| 国产欧美一级片| 国产精品福利一区二区三区| 欧美伦理视频在线观看| 亚洲大片精品免费| 久久青草精品视频免费观看| 国产偷拍一区二区| 亚洲精品久久久久久国产精华液| 国产精品久久久毛片| 波多野结衣一区| 国产精品激情av电影在线观看| 污视频软件在线观看| 亚洲国产精品综合小说图片区| 男生和女生一起差差差视频| 亚洲欧洲日韩| 91麻豆桃色免费看| av在线免费观看网址| 日韩视频国产视频| 久久机热这里只有精品| 成人激情视频网站| 丰满少妇久久久| 欧美大奶一区二区| 91精品国产91久久久久久不卡 | 99国产麻豆精品| 精品久久一二三| 亚洲丁香日韩| 日韩av成人在线观看| 蝌蚪视频在线播放| 欧美视频一区二区| 日韩三级在线观看视频| 国产sm精品调教视频网站| 成人网站免费观看入口| 欧美日韩直播| 国产精品久久久久久久久久久久| av中文字幕在线| 8v天堂国产在线一区二区| 久久老司机精品视频| www.亚洲色图.com| 男人天堂成人在线| 亚洲成av人片乱码色午夜| 国产精品国产精品| 国产精品极品美女在线观看| 色偷偷9999www| 欧美 日韩 国产 成人 在线| 欧美性高潮在线| 日本不卡一区视频| 成人性生交大片免费看中文| av动漫在线观看| 天天av综合| 久久国产精品免费一区| 久久av影院| 午夜精品久久久久久久白皮肤| 可以直接在线观看的av| 欧美一区二区三区白人| 亚洲va在线观看| 亚洲另类在线视频| av黄色免费网站| 国产精品亚洲第一| 日韩手机在线观看视频| 欧美a级在线| 日本成人黄色免费看| 日本在线成人| 国产精品视频一区二区高潮| 欧美高清另类hdvideosexjaⅴ| 亚洲日本中文字幕免费在线不卡| 精品黑人一区二区三区在线观看 | 男人的天堂99| 欧美影视一区| 午夜精品一区二区在线观看的 | 激情久久五月天| 欧美视频免费播放| 激情欧美丁香| 综合一区中文字幕| 国产成人精品三级高清久久91| 成人免费视频网站入口| 韩国精品视频在线观看| 欧美在线观看日本一区| 青草影视电视剧免费播放在线观看| 国产亚洲xxx| 色婷婷视频在线| 欧美一级日韩免费不卡| 中文字幕有码视频| 欧美午夜片在线免费观看| 黄页网站免费观看| 国产精品国产自产拍高清av| 黄色在线观看av| 成人白浆超碰人人人人| 性生活一级大片| 久久精品国产成人一区二区三区| 成人观看免费完整观看| 亚洲激情社区| 成人性生活视频免费看| 亚洲精品一二三区区别| 亚洲在线色站| 国产一区二区三区探花 | 久久人人99| 日韩中文一区二区三区| 欧美美乳视频| 欧美一区二区在线视频观看| 偷窥自拍亚洲色图精选| 久久精品第九区免费观看| 国产精品自在线拍| 国产一级特黄a大片99| gogo人体一区| 国产精品日韩一区二区免费视频| 视频一区中文字幕精品| 91精品黄色| 亚洲精品一二三**| 国产欧美日韩在线播放| ccyy激情综合| 国产中文一区二区| 日韩精品a在线观看91| 免费看成人午夜电影| 亚洲桃色综合影院| 欧美一区二区在线| 日韩欧美视频| 中文字幕一区二区中文字幕| 香蕉国产精品| 成年人视频大全| 亚洲小说欧美另类社区| 91成人在线观看喷潮教学| 国产欧美丝祙| 男女av免费观看| 美女在线视频一区| 国产成人在线综合| 国产精品91一区二区| 丰满少妇xbxb毛片日本| 91在线看国产| 国产午夜精品久久久久久久久| 国产精品电影一区二区| 欧美做爰啪啪xxxⅹ性| 亚洲最新视频在线观看| 日韩不卡视频在线| 欧美日韩视频不卡| 精品久久久中文字幕人妻| 精品日韩99亚洲| 日韩在线免费看| 色多多国产成人永久免费网站 | 免费在线观看日韩av| 不卡一二三区首页| 精品成人无码一区二区三区| 国产精品国产三级国产有无不卡 | 国产蜜臀一区二区打屁股调教| 97在线观看视频| 99riav视频一区二区| 亚洲最大福利视频网站| 亚洲精品动态| 特级西西444| 模特精品在线| 亚洲精品国产久| av成人动漫在线观看| 网爆门在线观看| 亚洲成av人片在www色猫咪| 做爰视频毛片视频| 精品国产乱码久久久久久久| 国产在线你懂得| 色综合久久久888| 肉色欧美久久久久久久免费看| 91久久国产婷婷一区二区| 任我爽精品视频在线播放| 伊人情人网综合| 老司机一区二区三区| 制服下的诱惑暮生| 国产精品久久久久婷婷| 精品美女久久久久| 91精品午夜视频| 成在在线免费视频| 91av视频在线观看| 奇米一区二区| 一区二区视频在线免费| 亚洲一区自拍| 亚洲v在线观看| 自拍av一区二区三区| 中文字幕精品视频在线观看| 精品国产凹凸成av人网站| 日本福利专区在线观看| 青青草原成人在线视频| 99久热这里只有精品视频免费观看| 亚洲国产一区二区精品视频 | 视频一区二区三区国产| 伦理中文字幕亚洲| 91精品影视| 久久久久久久久久久久久久一区 | 欧美日韩国产亚洲沙发| 欧美精品激情在线| 免费观看在线一区二区三区| 日韩视频在线观看国产| 国产精品婷婷| 久久无码专区国产精品s| 中文字幕综合网| 国产精品乱码一区二区| 中文字幕欧美日韩| 精品欧美一区二区三区在线观看| 国内一区在线| 999在线观看精品免费不卡网站| 少妇性l交大片7724com| ...xxx性欧美| 99国产精品久久久久99打野战| 中国日韩欧美久久久久久久久| 婷婷六月国产精品久久不卡| 久久一区二区三区av| 亚洲一区观看| 日韩精品卡通动漫网站| 欧美日韩国产在线播放| 色一情一乱一乱一区91av| 久久久免费av| 欧美日韩夜夜| 日韩网址在线观看| 国产视频一区二区在线| 欧美性猛交xxxx乱大交hd| 亚洲色图15p| 电影一区二区| 亚洲伊人婷婷| 国产一区不卡精品| 国产一级黄色av| 亚洲国产精品人人爽夜夜爽| 高清毛片在线观看| 蜜桃狠狠色伊人亚洲综合网站| 久久久久国产精品一区二区| 国产成人福利在线| 欧美日韩精品一区二区天天拍小说 | 午夜久久影院| 最好看的中文字幕| 亚洲国产精品一区二区久久| 人妻一区二区三区四区| 国产91精品高潮白浆喷水| 亚洲精品一级二级三级| 丰满少妇在线观看| 综合久久综合久久| 国产91免费看| 国产精品999999| 亚洲国产一区二区在线观看| 好吊操视频这里只有精品| 午夜精品aaa| 春暖花开成人亚洲区| 亚洲综合视频1区| 国产欧美一区二区色老头| 一级黄色录像毛片| 欧美一区二区三区系列电影| 成年人视频免费在线播放| 欧美不卡福利| 精品一区二区久久久| 国产无遮挡免费视频| 亚洲美女动态图120秒| 97精品资源在线观看| 欧日韩免费视频| 国产精品三级在线观看| 亚洲av无码国产综合专区| 秋霞av国产精品一区| 亚洲视频在线免费| 日韩精品卡通动漫网站| 欧美一卡二卡在线观看| 中文字幕在线直播| 偷拍盗摄高潮叫床对白清晰| 97se狠狠狠综合亚洲狠狠| 一级黄色a毛片| 96精品视频在线| 香蕉视频官网在线观看日本一区二区| www.四虎精品| 欧美三级视频在线播放| 韩日毛片在线观看| 992tv成人免费观看| 国产偷国产偷亚洲高清人白洁| 性做久久久久久久久久|