xmake v2.6.2 发布,新增 Linux 内核驱动模块构建支持 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
waruqi
V2EX    程序员

xmake v2.6.2 发布,新增 Linux 内核驱动模块构建支持

  waruqi
waruqi 2021-12-17 23:24:49 +08:00 2141 次点击
这是一个创建于 1394 天前的主题,其中的信息可能已经有所发展或是发生改变。

Xmake 是一个基于 Lua 的轻量级跨平台构建工具。

它非常的轻量,没有任何依赖,因为它内置了 Lua 运行时。

它使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt ,配置语法更加简洁直观,对新手非常友好,短时间内就能快速入门,能够让用户把更多的精力集中在实际的项目开发上。

我们能够使用它像 Make/Ninja 那样可以直接编译项目,也可以像 CMake/Meson 那样生成工程文件,另外它还有内置的包管理系统来帮助用户解决 C/C++ 依赖库的集成使用问题。

目前,Xmake 主要用于 C/C++ 项目的构建,但是同时也支持其他 native 语言的构建,可以实现跟 C/C++ 进行混合编译,同时编译速度也是非常的快,可以跟 Ninja 持平。

Xmake = Build backend + Project Generator + Package Manager 

新版本改动

这个版本主要新增两大特性:

  1. Linux 内核驱动模块的构建支持
  2. 分组构建和批量运行支持,可用于实现 Run all tests 功能

剩下的主要是一些零散的功能改进和 Bugs 修复,可以看下文末的更新内容明细,一些比较大的改动,下面也会逐一说明。

新特性介绍

构建 Linux 内核驱动模块

Xmake 也许是首个提供 Linux 内核驱动开发 内置支持的第三方构建工具了。

尽管网上也有介绍 CMake 如何去配置构建 linux 驱动,但是大都是通过 add_custom_command 方式自定义各种命令,然后执行 echo 去自己拼接生成 Linux 的 Makefile 文件。

也就是说,其实还是依赖 Linux 内核源码的 Makefile 来执行的构建,因此如果想自己追加一些编译配置和宏定义都会非常麻烦。

而使用 Xmake ,我们可以提供更加灵活的可配置性,更加简单的配置文件,以及一键编译、自动依赖拉取集成、Linux kernel 源码自动下载集成,内核驱动交叉编译等特性。

Hello World

我们通过一个最简单的字符驱动来直观感受下。

首先,我们准备一个 Hello World 驱动代码,例如:

add_requires("linux-headers", {cOnfigs= {driver_modules = true}}) target("hello") add_rules("platform.linux.driver") add_files("src/*.c") add_packages("linux-headers") set_license("GPL-2.0") 

它的配置非常简单,只需要配置上支持模块的 linux-headers 包,然后应用 platform.linux.driver 构建规则就行了。

然后直接执行 xmake 命令,一键编译,生成内核驱动模块 hello.ko 。

$ xmake [ 20%]: ccache compiling.release src/add.c [ 20%]: ccache compiling.release src/hello.c [ 60%]: linking.release build/linux/x86_64/release/hello.ko [100%]: build ok! 

简单么,也许你会说,这跟直接用 Makefile 配置,然后 make 编译也没啥太大区别么。

那么重点来了,Xmake 是会自动帮你拉取指定版本依赖内核源码,make 不会,CMake 也不会,用户必须通过 sudo apt install linux-headers-generic 自己安装它们。

但是 Xmake 不需要,上面的一键编译,我其实省略了部分输出,实际上是这样的。

$ xmake note: install or modify (m) these packages (pass -y to skip confirm)? in xmake-repo: -> m4 1.4.19 [from:linux-headers,bison,flex,elfutils] -> flex 2.6.4 [from:bc,linux-headers] -> bison 3.8.2 [from:bc,linux-headers] -> ed 1.17 [from:bc,linux-headers] -> texinfo 6.7 [from:bc,linux-headers] -> bc 1.07.1 [from:linux-headers] -> pkg-config 0.29.2 [from:linux-headers] -> openssl 1.1.1l [private, from:linux-headers] -> elfutils 0.183 [private, from:linux-headers] -> linux-headers 5.10.46 [driver_modules:y] please input: y (y/n/m) => download https://github.com/xmake-mirror/ed/archive/refs/tags/1.17.tar.gz .. ok => install ed 1.17 .. ok => download https://ftp.gnu.org/gnu/m4/m4-1.4.19.tar.xz .. ok => download https://ftp.gnu.org/gnu/texinfo/texinfo-6.7.tar.xz .. ok => download https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz .. ok => download https://github.com/openssl/openssl/archive/OpenSSL_1_1_1l.zip .. ok => install m4 1.4.19 .. ok => download https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz .. ok => install texinfo 6.7 .. ok => install pkg-config 0.29.2 .. ok => download http://ftp.gnu.org/gnu/bison/bison-3.8.2.tar.gz .. ok => install flex 2.6.4 .. ok => download https://sourceware.org/elfutils/ftp/0.183/elfutils-0.183.tar.bz2 .. ok => install elfutils 0.183 .. ok => install bison 3.8.2 .. ok => download https://ftp.gnu.org/gnu/bc/bc-1.07.1.tar.gz .. ok => install bc 1.07.1 .. ok => install openssl 1.1.1l .. ok => download https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.46.tar.xz .. ok => install linux-headers 5.10.46 .. ok [ 16%]: ccache compiling.release src/add.c [ 16%]: ccache compiling.release src/hello.c [ 16%]: ccache compiling.release src/hello.mod.c [ 66%]: linking.release build/linux/x86_64/release/hello.ko [100%]: build ok! 

首次编译,Xmake 会拉取所有依赖,如果用户自己已经通过 apt 等第三方包管理安装了它们,Xmake 也会优先用系统已经安装的版本,省去下载安装过程。

也就是说,不管在哪个环境,用户都不需要关心如何去搭建内核驱动开发环境,仅仅只需要一个 xmake 命令,就能搞定一切。

而这些依赖拉取,都是通过 add_requires("linux-headers", {cOnfigs= {driver_modules = true}}) 配置包来实现的。

另外,我们也可以看完整构建命令参数。

$ xmake -v [ 20%]: ccache compiling.release src/add.c /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functiOns=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"add\" -o build/.objs/hello/linux/x86_64/release/src/add.c.o src/add.c [ 20%]: ccache compiling.release src/hello.c /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kerne -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functiOns=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"hello\" -o build/.objs/hello/linux/x86_64/release/src/hello.c.o src/hello.c [ 60%]: linking.release build/linux/x86_64/release/hello.ko /usr/bin/ld -m elf_x86_64 -r -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.o build/.objs/hello/linux/x86_64/release/src/add.c.o build/.objs/hello/linux/x86_64/release/src/hello.c.o /usr/src/linux-headers-5.11.0-41-generic/scripts/mod/modpost -m -a -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/Module.symvers -e -N -T - WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functiOns=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod.o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod.c /usr/bin/ld -m elf_x86_64 -r --build-id=sha1 -T /usr/src/linux-headers-5.11.0-41-generic/scripts/module.lds -o build/linux/x86_64/release/hello.ko build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod.o 

使用特定版本的内核源码

我们也可以指定版本语义规则,选取自己需要的内核源码作为构建源。

add_requires("linux-headers 5.9.x", {cOnfigs= {driver_modules = true}}) 

交叉编译

我们也支持内核驱动模块的交叉编译,比如在 Linux x86_64 上使用交叉编译工具链来构建 Linux Arm/Arm64 的驱动模块。

我们只需要准备好自己的交叉编译工具链,通过 --sdk= 指定它的根目录,然后配置切换到 -p cross 平台, 最后指定需要构建的架构 arm/arm64 即可。

同样的,我们不用关心如何准备 linux-headers 去支持交叉编译,Xmake 的依赖包管理会帮你准本好一切,拉取构建支持对应架构的内核源码。

这里用到的交叉工具链,可以从这里下载: Download toolchains

更多,交叉编译配置文档,见:配置交叉编译

注:目前仅仅支持 arm/arm64 交叉编译架构,后续会支持更多的平台架构。

构建 Arm 驱动模块
$ xmake f -p cross -a arm --sdk=/mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf -c $ xmake [ 20%]: ccache compiling.release src/add.c [ 20%]: ccache compiling.release src/hello.c [ 60%]: linking.release build/cross/arm/release/hello.ko [100%]: build ok! 
构建 Arm64 驱动模块
$ xmake f -p cross -a arm64 --sdk=/mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu -c $ xmake [ 20%]: ccache compiling.release src/add.c [ 20%]: ccache compiling.release src/hello.c [ 60%]: linking.release build/cross/arm64/release/hello.ko [100%]: build ok! 

分组批量构建和运行支持

早期,我们已经支持了通过 set_group 设置目标分组,实现 vs/vsxmake 工程在 vs 下的源文件分组管理展示。

但是,这个分组仅限于这个特性,没有用于其他地方,而新版本中,我们继续改进利用分组特性,实现指定构建一批目标程序,以及批量运行一批目标程序。

这通常有什么用呢,比如可以用来提供 Run all testsRun all benchmarks 等功能。

编译指定一批目标程序

我们可以使用 set_group() 将给定的目标标记为 test/benchmark/... 并使用 set_default(false) 禁用来默认构建它。

这样,默认情况下 Xmake 不会去构建它们,但是我们可以通过 xmake -g xxx 命令就能指定构建一批目标程序了。

比如,我们可以使用此功能来构建所有测试。

target("test1") set_kind("binary") set_default(false) set_group("test") add_files("src/*.cpp") target("test2") set_kind("binary") set_default(false) set_group("test") add_files("src/*.cpp") 
$ xmake -g test $ xmake --group=test 

运行指定一批目标程序

我们也可以通过设置分组,来指定运行所有带有 test 分组的测试程序。

这通常非常有用,在此之前想要实现 Run all tests 功能,我们只能通过定义一个 task("tests") 在里面调用 os.exec 去挨个执行测试目标,配置比较繁琐,对用户要求比较高。

而现在,我们只需要对需要执行的测试目标程序标记为 set_group("test"),然后就可以批量运行它们了。

$ xmake run -g test $ xmake run --group=test 

支持分组模式匹配

另外,我们还可以支持分组的模式匹配,非常的灵活:

$ xmake build -g test_* $ xmake run -g test/foo_* $ xmake build -g bench* $ xmake run -g bench* 

更多信息见:#1913

改进 CMake 包源的查找和集成

之前的版本中,我们提供了 find_package("cmake::xxx") 来查找 cmake 内部的包,但是这种方式对于用户集成使用还是很繁琐。

因此,新版本中,我们进一步改进它,通过 add_requires 来实现统一快速的 cmake 包集成。

add_requires("cmake::ZLIB", {alias = "zlib", system = true}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") 

我们指定 system = true 告诉 xmake 强制从系统中调用 cmake 查找包,如果找不到,不再走安装逻辑,因为 cmake 没有提供类似 vcpkg/conan 等包管理器的安装功能, 只提供了包查找特性。

指定版本

add_requires("cmake::OpenCV 4.1.1", {system = true}) 

指定组件

add_requires("cmake::Boost", {system = true, cOnfigs= {compOnents= {"regex", "system"}})} 

预设开关

add_requires("cmake::Boost", {system = true, cOnfigs= {compOnents= {"regex", "system"}, presets = {Boost_USE_STATIC_LIB = true}}}) 

相当于内部调用 find_package 查找包之前,在 CMakeLists.txt 中预定义一些配置,控制 find_package 的查找策略和状态。

set(Boost_USE_STATIC_LIB ON) -- will be used in FindBoost.cmake find_package(Boost REQUIRED COMPONENTS regex system) 

设置环境变量

add_requires("cmake::OpenCV", {system = true, cOnfigs= {envs = {CMAKE_PREFIX_PATH = "xxx"}}}) 

指定自定义 FindFoo.cmake 模块脚本目录

mydir/cmake_modules/FindFoo.cmake

add_requires("cmake::Foo", {system = true, cOnfigs= {moduledirs = "mydir/cmake_modules"}}) 

相关 issues: #1632

xmake-idea 插件更新

xmake-idea 这个插件由于个人时间和精力的关系,一直没有花时间去维护更新,而 IDEA 插件的兼容性问题有非常多,只要一段时间不用,就无法在新的 Idea/Clion 上正常使用。

最近,我花了点时间,修复了一些兼容性问题,比如 Windows 上创建工程会卡死的问题,新版本 Clion 无法安装等问题。

目前,最新版本应该可以在全平台正常使用了。

另外一些值得提起的事情

年终总结

这是 2021 年我发布的最后一个版本,这一年下来,经历了很多,Xmake 也在逐渐成长为一个更加强大的构建工具。

到今年年底,Xmake 总共收获了 4.2k stars ,处理了 1.9k 的 issues/pr ,超过 8k 多次 commits 。

而官方的包管理仓库 xmake-repo 也已经收录了近 500+ 的常用依赖包。

感谢

感谢各位贡献者对 xmake-repo 仓库 和 Xmake 的贡献,完整贡献者列表见:Contributors

也非常感谢大家对 Xmake 的赞助的支持,使得我能够有足够的动力去持续维护,完整捐助列表见:Sponsors

更新内容

新特性

  • #1902: 支持构建 linux 内核驱动模块
  • #1913: 通过 group 模式匹配,指定构建和运行一批目标程序

改进

  • #1872: 支持转义 set_configvar 中字符串值
  • #1888: 改进 windows 安装器,避免错误删除其他安装目录下的文件
  • #1895: 改进 plugin.vsxmake.autoupdate 规则
  • #1893: 改进探测 icc 和 ifort 工具链
  • #1905: 改进 msvc 对 external 头文件搜索探测支持
  • #1904: 改进 vs201x 工程生成器
  • 添加 XMAKE_THEME 环境变量去切换主题配置
  • #1907: 添加 -f/--force 参数使得 xmake create 可以在费控目录被强制创建
  • #1917: 改进 find_package 和配置

Bugs 修复

  • #1885: 修复 package:fetch_linkdeps 链接顺序问题
  • #1903: 修复包链接顺序
6 条回复    2021-12-18 11:22:18 +08:00
dcoder
    1
dcoder  
   2021-12-18 04:11:59 +08:00
支持, 加油
sw0rd3n
    2
sw0rd3n  
   2021-12-18 09:52:04 +08:00 via iPhone
支持
fengjianxinghun
    3
fengjianxinghun  
   2021-12-18 10:36:14 +08:00
xmake 这个内核模块自动找依赖其实没有什么实用性,我看了 package 的 lua 代码,就是从内核网站上拉标准版本内核。
但现实中的开发都是编译某个特定发行版的特定内核版本,这功能 99%时候没用。。。。。

现在都靠 docker 来 build 了。
RUN wget -c https://vault.centos.org/7.5.1804/os/x86_64/Packages/kernel-3.10.0-862.el7.x86_64.rpm

其他更多细节就不说了,大部分时候还不如 makefile 方便。
feather12315
    4
feather12315  
   2021-12-18 10:56:22 +08:00 via Android
内核这种构件,做成黑盒工具并不友好。
编译 module 时候测试并不是针对一个版本构建的,需要手动指定 header 位置,需要定制 macro 参数,甚至指定编译器版本。

都开发内核了,还把构建工具当成黑盒使用,不清楚编译的调用流程,不够合格。
waruqi
    5
waruqi  
OP
   2021-12-18 11:20:07 +08:00 via Android
@fengjianxinghun 默认会优先使用系统上已有的版本,也可以自己指定路径用特定版本,拉取模式只是补充而已,可以用 也可以不用
waruqi
    6
waruqi  
OP
   2021-12-18 11:22:18 +08:00 via Android
@feather12315 这些都可以指定的,headers 路径,交叉编译工具链 编译参数等等 并不是黑盒 ,当然还有需要改进的地方
关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     889 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 27ms UTC 22:24 PVG 06:24 LAX 15:24 JFK 18:24
Do have faith in what you're doing.
ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86