Windows环境下基于CMake构建Lua
Windows环境下基于CMake构建Lua
- 环境
- `!!!注意: lua-5.4.6.tar.gz压缩包中,并未提供luac.c文件,无法构建luac.exe,可以从lua-5.4.5.tar.gz压缩包中拷贝使用`
- 一、搭建基于CMake构建的Lua环境
- 二、构建步骤
- 2.1 命令
- 2.2 验证
环境
Lua
下载地址
lua-5.4.6.tar.gz:https://www.lua.org/ftp/lua-5.4.6.tar.gz
lua-5.4.5.tar.gz:https://www.lua.org/ftp/lua-5.4.5.tar.gz
CMake
下载地址
官方:https://cmake.org/download/
!!!注意: lua-5.4.6.tar.gz压缩包中,并未提供luac.c文件,无法构建luac.exe,可以从lua-5.4.5.tar.gz压缩包中拷贝使用
一、搭建基于CMake构建的Lua环境
目录结构
5.4.6
├── build_vs2022_x64/ : 构建目录
├── dist/: 安装目录
├── src/: 源码目录
└── CMakeLists.txt
CMakeLists.txt
!!!相关配置根据源码包中的makefile文件和luaconf.h头文件确定
cmake_minimum_required(VERSION 3.20)# 解决方案名字:lua
# 版本规则:主版本.子版本.修订版本.日期-阶段版本
# 构建语言:C/C++
# 构建目录:默认值为${PROJECT_BINARY_DIR}
project(luaVERSION 0.0.0.20240430LANGUAGES C CXX
)# 使用C99标准 参照源码中的makefile选项:-std=c99
# 设为True:如果编译器不支持C99标准 构建过程报错停止
# 方式一:利用set指定 the C++ standard
#set(CMAKE_C_STANDARD_REQUIRED True)
#set(CMAKE_C_STANDARD 99)
# 方式二:利用接口库的形式
# https://cmake.org/cmake/help/latest/prop_gbl/CMAKE_C_KNOWN_FEATURES.html
add_library(project_c_compiler_flags INTERFACE)
target_compile_features(project_c_compiler_flags INTERFACE c_std_99)# 设置debug模式的标记
set(CMAKE_DEBUG_POSTFIX d)# 判断当前使用的编译环境
# 基于生成器表达式
# $<COMPILE_LANG_AND_ID:language,compiler_ids>: 当项目所用编程语言匹配language,
# 且项目所用编译器匹配compiler_ids时,此表达式返回1,否则返回0
set(gcc_like_c "$<COMPILE_LANG_AND_ID:C,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_like_c "$<COMPILE_LANG_AND_ID:C,MSVC>")# 为接口库project_c_compiler_flags 增加编译警告选项
# INTERFACE作用域表示project_c_compiler_flags 本身不需要,但是project_c_compiler_flags调用方需要使用
# 我们只想在编译阶段使用警告选项,而不会在安装阶段使用,可以利用BUILD_INTERFACE限制
# GCC警告说明:
# - 适用于C和C++的警告标志
# -Wall: 启用大多数警告
# -Wfatal-errors: 将所有警告视为错误,导致编译失败
# -Wextra: 启用额外的警告,包括未使用的参数、未使用的函数返回值等
# -Wshadow: 警告声明变量时隐藏了外层作用域中的变量
# -Wundef: 警告使用未定义的宏
# -Wwrite-strings: 警告将字符串常量赋值给非const char指针
# -Wredundant-decls: 警告冗余的声明
# -Wdisabled-optimization: 警告禁用的优化
# -Wdouble-promotion: 警告双精度提升
# -Wmissing-declarations: 警告未声明的函数
# -Wformat=2:则启用了与格式化字符串相关的严格警告
# -Wunused:警告未使用的代码元素
# - 不适用于C++的警告标志
# -Wdeclaration-after-statement: 警告在语句之后声明变量
# -Wmissing-prototypes: 警告缺少函数原型
# -Wnested-externs: 警告嵌套的extern声明
# -Wstrict-prototypes: 警告函数原型不符合严格的C标准
# -Wc++-compat: 警告C++风格的兼容性问题
# -Wold-style-definition: 警告使用旧的函数定义风格
# MSVC警告说明:
# 基于等级的警告系统:/W1, /W2, /W3, /W4
# - /W4 通常被认为是最严格的警告级别,类似于GCC的 -Wall 和 -Wextra 的组合
# -Wfatal-errors (GCC/Clang) 类似对应:/WX
set(GCC_C_WARNING_FLAGS "-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused")
set(MSVC_C_WARNING_FLAGS "/W4")
target_compile_options(project_c_compiler_flags INTERFACE "$<${gcc_like_c}:$<BUILD_INTERFACE:${GCC_C_WARNING_FLAGS}>>""$<${msvc_like_c}:$<BUILD_INTERFACE:${MSVC_C_WARNING_FLAGS}>>"
)# 设置编译器标志
# GCC编译选项说明:
# -std=c99:GCC按照C99标准编译代码
# -O2: 编译器优化选项,表示使用第二级优化
# -fno-stack-protector:指示编译器禁用堆栈保护器。当这个选项被设置时,编译器不会为函数生成堆栈保护代码,这可能会提高代码的执行效率,但也会降低对堆栈溢出攻击的防护能力。
# -fno-common:指示编译器不要使用 common 段来存储未初始化的全局变量。通常,未初始化的全局变量会被放置在 common 段中,并在链接时合并。
# 禁用这个特性会导致每个未初始化的全局变量都有自己的存储空间,这可能会增加最终二进制文件的大小。这个选项有助于确保代码的行为更加确定,特别是在涉及多个编译单元和未初始化全局变量时。
# -march=native:编译器针对当前运行的机器生成优化的机器代码。它会利用当前机器的特定指令集和功能来生成代码,从而可能提高程序的执行速度。
# 然而,这也会降低代码在其他硬件上的可移植性,因为生成的代码可能依赖于当前机器的特定功能。
# MSVC编译选项说明:
# -std=c99 (GCC/Clang) 类似对应:/std:c99
# -O2 (GCC/Clang) 类似对应:/O2
# /Ox:启用最大优化,这通常是 /O2 和其他优化选项的组合
# /Od:禁用所有优化
# /Zc:inline:强制内联函数的内联
# /Zi: 生成调试信息
# /Gm-:禁用最小重新生成
# /EHsc: 选择 C++ 异常处理模型
# /RTC1: 启用运行时检查
# /arch:SSE2、/arch:AVX:MSVC需要手动指定目标处理器架构和指令集,不会像GCC的-march=native自动检测。
# GCC
# 在当前的 GCC_C_COMPILER_FLAGS 值的基础上追加 -std=c99 编译选项,然后更新 GCC_C_COMPILER_FLAGS 变量的值
set(GCC_C_COMPILER_FLAGS "${CMAKE_C_FLAGS}")
# 编译选项 参照源码中的makefile选项
set(GCC_C_COMPILER_FLAGS "${GCC_C_COMPILER_FLAGS} -std=c99;-O2;-fno-stack-protector;-fno-common;-march=native")
# MSVC
# 在当前的 MSVC_C_COMPILER_FLAGS 值的基础上追加 /std:c99 编译选项,然后更新 MSVC_C_COMPILER_FLAGS 变量的值
set(MSVC_C_COMPILER_FLAGS "${CMAKE_C_FLAGS}")
set(MSVC_C_COMPILER_FLAGS "${MSVC_C_COMPILER_FLAGS} /std:c99")
# target_compile_options: 专注于设置编译器的行为或特性,比如优化级别、警告级别、语言标准等。
target_compile_options(project_c_compiler_flags INTERFACE "$<${gcc_like_c}:$<BUILD_INTERFACE:${GCC_C_COMPILER_FLAGS}>>""$<${msvc_like_c}:$<BUILD_INTERFACE:${MSVC_C_COMPILER_FLAGS}>>"
)
# target_compile_definitions: 专注于定义编译器中的宏,这些宏可以在源代码中使用。
# -Dxxx表示宏定义
# 配置都是来自makefile
target_compile_definitions(project_c_compiler_flags INTERFACE"$<${gcc_like_c}:$<BUILD_INTERFACE:-DLUA_USE_LINUX;-DLUA_USE_READLINE>>"
)# 编译dll选项:
# LUA_BUILD_AS_DLL 默认为ON 生成windows的dll
option(LUA_BUILD_AS_DLL "Build for Windows dll." OFF)
if(LUA_BUILD_AS_DLL)# 源码文件luaconf.h中,定义宏LUA_BUILD_AS_DLL用来确定Windows dll的生成定义target_compile_definitions(project_c_compiler_flags INTERFACE "$<${msvc_like_c}:$<BUILD_INTERFACE:-DLUA_BUILD_AS_DLL>>") message(STATUS "LUA_BUILD_AS_DLL=ON")
endif()# 源码目录
file(GLOB LUA_LIB_SRC ${PROJECT_SOURCE_DIR}/src/*.h ${PROJECT_SOURCE_DIR}/src/*.c)
set(LUA_EXE_SRC ${LUA_LIB_SRC})
set(LUAC_EXE_SRC ${LUA_LIB_SRC})
set(LUA_EXE_MAIN ${PROJECT_SOURCE_DIR}/src/lua.c)
set(LUAC_EXE_MAIN ${PROJECT_SOURCE_DIR}/src/luac.c)
set(ONE_LUA_MAIN ${PROJECT_SOURCE_DIR}/src/onelua.c)
# liblua 不包含 luac.c luac.c
list(REMOVE_ITEM LUA_LIB_SRC ${LUA_EXE_MAIN} ${LUAC_EXE_MAIN} ${ONE_LUA_MAIN})
# lua.exe 不包含 luac.c
list(REMOVE_ITEM LUA_EXE_SRC ${LUAC_EXE_MAIN} ${ONE_LUA_MAIN})
# luac.exe 不包含 lua.c
list(REMOVE_ITEM LUAC_EXE_SRC ${LUA_EXE_MAIN} ${ONE_LUA_MAIN})
#message(STATUS "【${LUAC_EXE_SRC}】")# 版本
set(LUA_VERSION 050406)
#set(LUA_LIB lib${PROJECT_NAME}${LUA_VERSION})
#set(LUA_EXE ${PROJECT_NAME}${LUA_VERSION})
#set(LUAC_EXE ${PROJECT_NAME}c${LUA_VERSION})
set(LUA_LIB lib${PROJECT_NAME})
set(LUA_EXE ${PROJECT_NAME})
set(LUAC_EXE ${PROJECT_NAME}c)message(STATUS "Lua Library Name:" ${LUA_LIB})
message(STATUS "Lua Executable Name:" ${LUA_EXE})
message(STATUS "Luac Executable Name:" ${LUAC_EXE})
message(STATUS "Lua Project Dir:" ${PROJECT_SOURCE_DIR})# 创建目标
add_library(${LUA_LIB} ${LUA_LIB_SRC})
add_executable(${LUA_EXE} ${LUA_EXE_SRC})
add_executable(${LUAC_EXE} ${LUAC_EXE_SRC})# 链接
# 包含添加目录
# ${PROJECT_SOURCE_DIR}: 表示项目主CMakeLists.txt文件所在目录
target_include_directories(${LUA_LIB} PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>)
target_include_directories(${LUA_EXE} PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>)
target_include_directories(${LUAC_EXE} PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>)# 链接上述绑定的接口库
target_link_libraries(${LUA_LIB} PRIVATE project_c_compiler_flags)
target_link_libraries(${LUA_EXE} PUBLIC project_c_compiler_flags)
target_link_libraries(${LUAC_EXE} PUBLIC project_c_compiler_flags)# 生成结果后 需要安装
# 配置默认安装路径
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/dist)
endif()# EXPORT可以导出其他CMake项目直接使用此项目的信息
# 导出后可以将信息输出到xxx.cmake文件,方便其它项目find_package调用
# ${CMAKE_INSTALL_INCLUDEDIR}: include
# make cache variables for install destinations
include(GNUInstallDirs)
message(STATUS "Lua install dir:" ${CMAKE_INSTALL_PREFIX})
message(STATUS "Lua install include dir:" "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}")set(LIBLUA_INTSALL_TARGET ${LUA_LIB}-Targets)
# INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}"
install(TARGETS ${LUA_LIB}EXPORT ${LIBLUA_INTSALL_TARGET}LIBRARY DESTINATION $<IF:$<CONFIG:Debug>,libd,lib>ARCHIVE DESTINATION $<IF:$<CONFIG:Debug>,libd,lib>INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}"
)
# 安装include
install(FILES ${PROJECT_SOURCE_DIR}/src/lauxlib.h ${PROJECT_SOURCE_DIR}/src/lua.h ${PROJECT_SOURCE_DIR}/src/luaconf.h ${PROJECT_SOURCE_DIR}/src/lualib.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}")# 关于exe install
install(TARGETS ${LUA_EXE}EXPORT ${LUA_EXE}-TargetsRUNTIME DESTINATION $<IF:$<CONFIG:Debug>,bind,bin>
)
install(TARGETS ${LUAC_EXE}EXPORT ${LUAC_EXE}-TargetsRUNTIME DESTINATION $<IF:$<CONFIG:Debug>,bind,bin>
)
二、构建步骤
2.1 命令
> cd @lua-5.4.6.tar.gz解压目录
> cmake -S . -B .\build_vs2022_x64\ -G "Visual Studio 17 2022"
> cmake --build .\build_vs2022_x64\ --config Debug -v --clean-first
> cmake --install .\build_vs2022_x64\ --prefix .\dist --config Debug -v
2.2 验证
> cd @lua-cmake-install目录
> lua -v
> Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio
相关文章:
Windows环境下基于CMake构建Lua
Windows环境下基于CMake构建Lua 环境!!!注意: lua-5.4.6.tar.gz压缩包中,并未提供luac.c文件,无法构建luac.exe,可以从lua-5.4.5.tar.gz压缩包中拷贝使用 一、搭建基于CMake构建的Lua环境二、构…...
LLM⊗KG范式下的知识图谱问答实现框架思想阅读
分享一张有趣的图,意思是在分类场景下,使用大模型和fasttext的效果,评论也很逗。 这其实背后的逻辑是,在类别众多的分类场景下,尤其是在标注数据量不缺的情况下,大模型的收益是否能够比有监督模型的收益更多…...
ue引擎游戏开发笔记(35)——为射击添加轨道,并显示落点
1.需求分析: 我们只添加了开枪特效,事实上并没有实际的效果产生例如弹痕,落点等等。所以逐步实现射击的完整化,先从实现落点开始。 2.操作实现: 1.思路:可以这样理解,每次射击的过程是一次由摄…...
路由策略与路由控制
1.路由控制工具 匹配工具1:访问控制列表 (1)通配符 当进行IP地址匹配的时候,后面会跟着32位掩码位,这32位称为通配符。 通配符,也是点分十进制格式,换算成二进制后,“0”表示“匹配…...
JAVA版本的ATM编程问题记录
前段时间用C语言写了个银行ATM系统,还写了一篇文章记录了一些,C语言的ATM文章。后来又用IDEA写了一个JAVA版本的银行ATM。有人就会问为啥浪费这个时间写ATM呢?🧐其实是我本科代码没学好,所以现在想利用比较熟悉的ATM系…...
C#winfrom三层架构实现简单课程管理系统管理系统,三层架构实现增删改查
1. 项目展示 1.1登录展示 1.2添加课程信息展示 1.3课程信息管理-查询-修改-删除 1.4修改登录密码 2.项目功能介绍(图) 3.数据库设计 3.1 教师表设计 3.2 课程分类表 3.3 课程信息表 4. 创建样式界面 winfrom 超详细UI创建过程 实现双色球选号器UI界面…...
AI技术赋能下的视频监控方案是如何解决新能源汽车充电难问题的?
一、方案背景 刚刚结束的第十八届北京车展异常火爆,其中一组与汽车有关的数字让人格外关注。根据乘联会2024年4月19日公布的最新数据,全国乘用车市场零售达到51.6万辆,其中新能源车的销量约为26万辆,市场渗透率达到50.39%。 这意味…...
长难句打卡5.6
For H&M to offer a $5.95 knit miniskirt in all its 2,300-plus stores around the world, it must rely on low-wage overseas labor, order in volumes that strain natural resources, and use massive amounts of harmful chemicals. 翻译:H&M若要在其全球总共2…...
PDF编辑器软件常见问题(技巧)总结
目录 问题pdf高级编辑器中编辑文字时" 格式 " 如何出现? 待续、更新中 问题 pdf高级编辑器中编辑文字时" 格式 " 如何出现? shiftF4 待续、更新中 1 顿号“、” : 先使用ctrl. 再使用一遍切回 2 下标: 在数字两边加上 ~ 即可 , 21 3 上标: 2 0 2^{0} …...
Dive into Deep Learning-优化算法(1)
优化和深度学习的关系 优化是最小化损失函数,而深度学习的目标是在给定有限数据量的情况下寻找合适的模型,分别对应着训练误差和泛化误差;需要注意过拟合; 优化面临的挑战(求解数值解) 局部最小值&#…...
Partisia Blockchain 生态首个zk跨链DEX现已上线
在5月1日,由Partisia Blockchain与zkCross创建合作推出的Partisia zkCrossDEX在Partisia Blockchain生态正式上线。Partisia zkCrossDEX是Partisia Blockchain上重要的互操作枢纽,其融合了zkCross的zk技术跨链互操作方案,并利用Partisia Bloc…...
.NET操作 Access (MSAccess)
注意:新项目推荐 Sqlite ,Access需要注意的东西太多了,比如OFFICE版本,是X86还是X64 连接字符串 ProviderMicrosoft.ACE.OleDB.15.0;Data Source"GetCurrentProjectPath"\\test.accdb//不同的office版本 连接字符串有…...
shell脚本,删除30天以前的日志,并将日志推送到nas,但运行出现/bin/bash^M。
删除30天以前的日志 将日志推送到nas中,然后删除pod中的日志 pod挂载到本地 运行出现/bin/bash^M 1、删除30天以前的日志: #! /bin/bash# 定义源日志目录 LOG_DIR/home/log/ # 删除日志 find $LOG_DIR -type f -name "*.log" -mtime 30 -exec…...
现身说法暑期三下乡社会实践团一个好的投稿方法胜似千军万马
作为一名在校大学生,去年夏天我有幸参与了学院组织的暑期大学生三下乡社会实践活动,这段经历不仅让我深入基层,体验了不一样的生活,更是在新闻投稿的实践中,经历了一次从传统到智能的跨越。回忆起那段时光,从最初的邮箱投稿困境,到后来智慧软文发布系统的高效运用,每一步都刻印…...
小程序账号设置以及request请求的封装
一般开发在小程序时,都会有测试版和正式版,这样在开发时会比较方便。 在开发时。产品经理都会给到测试账号和正式账号,后端给的接口也都会有测试环境用到的接口和正式环境用到的接口。 这里讲一讲我这边如何去做的。 1.在更目录随便命名一…...
怎么解决端口被占用
目录 一、引言 二、解决方法 一、引言 最近用vscode写网页,老是遇见端口被占用,报错如下: listen tcp :8080: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted. 二、解决方法 1.换…...
JavaScript 循环方法详解
在编程中,循环是一种重复执行代码块的机制,直到满足某个条件为止。JavaScript 提供了多种循环结构来帮助我们实现这一功能。以下是 JavaScript 中常用的几种循环方法的详细解释。 1. for 循环 for 循环是 JavaScript 中最常用的循环结构之一。它使用一…...
树莓派4b测量PM2.5
1.GP2Y1010AU0F粉尘传感器连接图 2. GP2Y1010AU0F工作原理 工作原理 传感器中心有个洞可以让空气自由流过,定向发射LED光,通过检测经过空气中灰尘折射过后的光线来判断灰尘的含量。 3.源代码 main.py # coding=UTF-8 import RPi.GPIO as GPIO from ADC import ADS1015…...
恒生电子,快手25届实习内推
恒生电子,快手25届实习内推 ①快手 【岗位】算法、工程、游戏,产品运营、市场、职能等 【一键内推】https://campus.kuaishou.cn/recruit/campus/e/h5/#/campus/jobs?codecampuswQrLOMvHE 【内推码】campuswQrLOMvHE ②恒生电子 【招聘岗位】JAVA、测试…...
蓝桥杯练习系统(算法训练)ALGO-949 勇士和地雷阵
资源限制 内存限制:256.0MB C/C时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s 问题描述 勇士们不小心进入了敌人的地雷阵(用n行n列的矩阵表示,*表示某个位置埋有地雷,-表示某个…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...
云原生安全实战:API网关Envoy的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口,负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...
GeoServer发布PostgreSQL图层后WFS查询无主键字段
在使用 GeoServer(版本 2.22.2) 发布 PostgreSQL(PostGIS)中的表为地图服务时,常常会遇到一个小问题: WFS 查询中,主键字段(如 id)莫名其妙地消失了! 即使你在…...
