找回密码
 注册
查看: 4310|回复: 0

openstack 使用pbr配置,setup.cfg的格式与含义

[复制链接]

1

主题

0

回帖

12

积分

管理员

积分
12
QQ
发表于 2019-10-15 11:32:30 | 显示全部楼层 |阅读模式
long_description
+ \  Y) F' n" ?3 p! [. eThere is no need to maintain two long descriptions- and your README file is probably a good long_description. So we’ll just inject the contents of your README.rst, README.txt or README file into your empty long_description. Yay for you.3 h3 r0 p3 P1 b2 S8 w( i% ?
9 m* `3 o& W7 q1 O9 U0 Q6 @; N7 Y
日志描述! Z7 ~# Z/ P0 q* d

; B" r! R6 V7 _# `5 }& J8 `没有必要维护两个详细描述-你的README文件就应该是很好的描述。所以我们将你的README.rst,或者README.txt 或者README文件中的内容提取出来,作为项目描述。
6 A* g+ Q# C- n0 e. k* c8 ]
. ^+ o, W. S# O0 K' p- |; QUsage
- v3 u+ ]/ u. S$ C7 npbr is a setuptools plugin and so to use it you must use setuptools and call setuptools.setup(). While the normal setuptools facilities are available, pbr makes it possible to express them through static data files.
- E: x5 D3 s5 y- Y
/ f4 N1 L$ E2 i* U# W$ o' `使用
7 G" W/ B# F. o2 S1 y8 C* U* ?; Q8 e2 O9 V4 f" b
pbr是setuptools的插件,所以你必须使用setuptools,然后调用setuptools.setup()函数。当普通的setuptools设施可用,pbr通过静态的数据文档使得需求被更加清晰表达。/ j" Z! v+ c1 _# Y" g4 `( K# N" D
, s! |5 P% L0 D- S1 y6 U& y0 R; B7 w
setup.py3 D) y+ O  E  o( p2 K  C9 n7 ]' e$ K
pbr only requires a minimal setup.py file compared to a standard setuptools project. This is because most configuration is located in static configuration files. This minimal setup.py file should look something like this:1 ^/ D  `" i& n2 \
6 T/ L' J3 Y8 }4 c  V- K$ g
pbr只需要最小化的setup.py 文件,跟普通的使用setuptools的项目相比。这是因为设置都在setup.cfg里面。setup.py文件如下。3 f6 W4 W" l; j
$ U2 y% {( f0 Q4 \
复制代码8 O& O) A0 W  e. R5 M' T
#!/usr/bin/env python8 S6 A* G6 P/ k: n

0 g% v, p% r- z- p) Xfrom setuptools import setup$ L, ^8 ^6 h+ h3 m1 H
8 `  g5 T/ D* I* O5 Y
setup(
: e, A: M2 F6 z    setup_requires=['pbr'],9 J6 q" o1 m# C0 H
    pbr=True,) `6 Q2 B" h8 R
)4 X# x! F* y* K
复制代码. v; R1 b- D5 R; m6 j
, |! G# X1 R& E" H7 }% P% S

1 P1 G& G/ |' _& Y' u' Z6 H$ }Note1 V" m; g6 r% D, d6 v
3 v( q2 w1 b) f
It is necessary to specify pbr=True to enabled pbr functionality.
8 h1 h, X1 v7 J$ [3 n& [7 x
( m7 U# P. c$ nNote; ~* `$ w) a4 x- ~: ^4 q& ^9 ~

- X: u- z! ?9 L9 ]) eWhile one can pass any arguments supported by setuptools to setup(), any conflicting arguments supplied in setup.cfg will take precedence.
3 q- k7 r3 K* e# s0 I, _8 z; I/ w+ b: U, F$ ^3 W' p' w
注意:) {, u7 q1 n- Y7 L+ |, f
必须设置pbr=True,来使用pbr.
& T; U5 }$ |1 |8 V# }
9 [, r; o3 ~4 ^7 R% g& J当使用setuptools的setup函数来进行设置时,如果与位于setup.cfg中的信息产生冲突,则setup.cfg则优先。* C, X7 h/ z$ h+ L
* J+ X9 W" b6 w! _- P: G
setup.cfg4 U6 V+ B7 r. m2 I* o/ u* O1 ^
The setup.cfg file is an ini-like file that can mostly replace the setup.py file. It is based on the distutils2 setup.cfg file. A simple sample can be found in pbr‘s own setup.cfg (it uses its own machinery to install itself):8 x2 \. J5 W1 ]7 [4 @# u! V2 K
$ e9 {/ J4 x0 |, c. s
setup.cfg文档  ]- x, e8 V6 d/ M9 ?, @! H
7 X. p# _1 C1 G3 v. v
这个文档像ini 文档。它基于项目distutils2的setup.cfg文件设置。下面是一个例子(这个设置中项目使用自己的机制去安装)9 J9 l1 d  t( i) f# I! [. ]" I

: A, Q1 c) J  b; L, \1 t8 |. z复制代码( u- i# T  `6 r9 \+ h
[metadata]
3 S: G6 h2 [8 G( g- M3 r8 {name = pbr  L- j; E) K0 m% T& i5 [
author = OpenStack Foundation
& j' K  @$ n5 g+ i% X, f" qauthor-email = openstack-dev@lists.openstack.org. {8 t6 ]* o4 O/ n
summary = OpenStack's setup automation in a reusable form& {" Y  q# v( O
description-file = README, b; `4 s7 \3 G, ~; q. n
home-page = https://launchpad.net/pbr) u; j! y2 p8 _/ l. V: [
license = Apache-24 {- m; A5 U: }' E1 z+ |5 n, U- k
classifier =
# e/ B' R3 x9 q1 k( M    Development Status :: 4 - Beta; V/ w% Z" f& D
        Environment :: Console
% i& m/ T: V/ n7 z" Q% W* d        Environment :: OpenStack/ m6 `% {# ]3 c' d9 O2 Z
        Intended Audience :: Developers
3 Z& r+ b; f( I% n7 s        Intended Audience :: Information Technology
  `: ?( I1 z, b; ]' e        License :: OSI Approved :: Apache Software License5 w0 d' X# z0 ?% v0 F8 g- p
        Operating System :: OS Independent
( u. ~& E% T  x* h        Programming Language :: Python
$ a5 r: m% W( a* P% N: m8 okeywords =0 y1 f; e0 I2 Q. I/ \
    setup
) l9 e; m2 `' k% h2 q; A    distutils$ r! K% x( U9 E
[files]
  R" K# k2 @2 A. V" mpackages =
& t- C7 Y( u. Y6 E3 C# q' J* M    pbr
7 e4 T# x# p9 y6 P6 y( zdata_files =- h2 c5 U$ ]+ e) Q% d- }
    etc/pbr = etc/*2 \/ s& \2 ^1 [7 n) h
    etc/init =% `4 `3 d# p  d6 K* z- R
        pbr.packaging.conf2 V. |& B( l' h! a0 K$ s
        pbr.version.conf, T! U6 ~* A$ I! n+ M$ v
[entry_points], d4 q/ N# R5 o5 w
console_scripts =; X3 v1 ^( {) k1 @% S3 Q
    pbr = pbr.cmd:main2 q$ r# x- Y2 C+ F- e
pbr.config.drivers =2 m7 y1 k8 S2 z0 {+ R
    plain = pbr.cfg.driver:Plain9 y- F( D) j1 O8 z
复制代码. Y* q5 Z4 @0 f& `& T
( n9 I' D7 R& ?

4 X8 n- A. j, C0 Z2 ]There are a number of sections in these documents. These are:
; O, G5 W( q, b  J9 ^' V
4 }+ ]& \2 C) D2 Vmetadata
3 c! V/ |5 \2 z" w( c- [% n$ wfiles
1 y0 f) ~( v. ~/ w$ a' s' a: Y. Bentry_points( d" e& o8 R- E$ U- Z. j
pbr2 D- X8 f' r+ g3 V! ~9 _
setup.cfg文件中有几个段落:# {+ o1 |! V9 b9 O4 f
metadata- G, P: \4 \9 w
files
9 E& m5 S; s. e6 Centry_points
* |( E/ U7 x6 A' G) ~pbr
: y- u/ i' ^0 W7 H- h& ?; f5 L: E 3 R6 ~0 V, @$ `& c

) u+ R7 ~6 }/ ?9 x- v. Afiles
9 r, b. f7 d( HThe files section defines the install location of files in the package using three fundamental keys: packages, namespace_packages, and data_files.
' w8 _' ]4 l7 D" Q8 I% x4 \4 k. l; _) i% I8 a
packages is a list of top-level packages that should be installed. The behavior of packages is similar to setuptools.find_packages in that it recurses the python package hierarchy below the given top level and installs all of it. If packages is not specified, it defaults to the value of the name field given in the [metadata] section.
) D) _" O3 L# m. P  d  d. _8 E' i* a% j  d( T' M
namespace_packages is the same, but is a list of packages that provide namespace packages.' D3 v/ v( ]. J3 {

! Y. I& l! p5 u8 Wdata_files lists files to be installed. The format is an indented block that contains key value pairs which specify target directory and source file to install there. More than one source file for a directory may be indicated with a further indented list. Source files are stripped of leading directories. Additionally, pbr supports a simple file globbing syntax for installing entire directory structures, thus:) i0 a( S* n' L

! N. v& U4 Y/ k段落 files5 Q: |7 |4 k* p5 }) C3 M3 W+ o

  b% \# t) a8 f' _$ |0 q& u" U1 Y  dfiles段落定义了包中的文件位置,有三个基本的设置键:packages,namespace_packages,以及data_files.
# L, K2 L1 |9 u4 l9 e  i! ]& b3 R$ U# \* _* u- I
packages指定了必须安装的数个最高级别的包的列表。这个像setuptools中的函数find_packages.这里面它进入python的包体系中,在最高的级别路径下安装它。如果packages没有被指明,则默认为metadata段落中的name键值。0 a5 J* @$ b" O' o
! b+ x: ?# y7 E* ^, L$ A: z7 T7 V% Y
复制代码
9 g: |. [4 E- O5 _+ m[files]3 v5 A  `# r7 j
data_files =- y: U, s. x& L/ K
    etc/pbr = etc/pbr/*
+ v) v  c' D. S    etc/neutron =1 T0 J3 u# x' Z9 F4 s+ y
        etc/api-paste.ini
/ K- A$ x" a# `; V+ [        etc/dhcp-agent.ini. I8 m" h% A3 K' `, Z% C
    etc/init.d = neutron.init
) A* s( ^5 s3 Z0 o4 V复制代码+ `! @3 c6 E: a7 ]  {' \5 Z) _* i
% a8 `7 H& d: M$ j& w  z7 u0 u

" O- |+ Z* `8 W; @+ @( Mwill result in /etc/neutron containing api-paste.ini and dhcp-agent.ini, both of which pbr will expect to find in the etc directory in the root of the source tree. Additionally, neutron.init from that dir will be installed in /etc/init.d. All of the files and directories located under etc/pbr in the source tree will be installed into /etc/pbr.
6 Y: ~9 p" `8 {* n/ ?' o! S! S3 m4 W/ W1 f: v$ X+ E
Note that this behavior is relative to the effective root of the environment into which the packages are installed, so depending on available permissions this could be the actual system-wide /etc directory or just a top-level etc subdirectory of a virtualenv." |9 g  x. D6 e- i6 T

* j- X; L7 a' h" n( X! M" r9 [上面的配置会 将本项目的etc/api-paste.ini拷贝/etc/neutron 文件夹中,而将项目中的etc/pbr中的所有文件拷贝到/etc/pbr中。项目中的neutron.init将会拷贝到/etc/init.d中。
) _+ i+ W$ [7 h6 D! j# _注意:7 p' y, d9 J- q8 `4 J

* j/ k4 }8 d! D这种行为是根据在那个环境下包被安装,以及它们相应的根目录。(也就是说拷贝也许不是到/etc/neutron中,或者某个子目录下面的etc/neutron中)。所以基于权限,文件会被拷贝到根目录下的etc或者某个子目录下的etc1 c* Z% S8 {) \( o

) Z7 m' m# q  p" Yentry_points
. D: H! G7 r/ [  w7 G: UThe entry_points section defines entry points for generated console scripts and python libraries.6 a$ ?# S: R2 U& u, ?
. o# G+ Q( }$ C' T9 P4 F& u: D7 I
The general syntax of specifying entry points is a top level name indicating the entry point group name, followed by one or more key value pairs naming the entry point to be installed. For instance:- D* G, r( _# ~! F3 y/ p
9 Q$ t5 m8 R4 @1 |6 x; [
段落entry_points
% x/ p* d' Y  q& Z9 t
3 z0 ]$ X, v: }% W( e7 Q/ G这个段落定义了命令行命令以及python的库lib的进入点。
& z7 j- t$ G7 k6 S2 J9 W  Y6 c0 f. |0 i$ g0 ], ^, l
里面的内容分成子段落,子段落的头行设置了一组进入点的最高级目录的名称,里面定义了键值对,描述了会被安装的进入点。3 M* K; o: G/ `3 V5 l8 k* r: J
" t2 Y! ~1 c+ q0 ~4 r
复制代码7 s" y% g. ~( g- n  b- X2 w
[entry_points]
) A* l) Y1 d" ^& W2 B8 s& y! Q2 Lconsole_scripts =
* \; K9 N! p( u$ M$ w" k    pbr = pbr.cmd:main# V# z  Z* d  E  g' q% \5 j
pbr.config.drivers =# H* F, l5 p7 Y. m" G$ @6 g1 F  |
    plain = pbr.cfg.driver:Plain
' a9 Y/ w5 C& G/ ~    fancy = pbr.cfg.driver:Fancy. Y  s0 z$ p0 x5 C- v/ C+ w/ m2 D" W
复制代码
' U" j6 E' o; ^! T4 U5 s  F
8 X9 b' R' d: g- ^7 Q
. R4 e: J+ r& u( d- |Will cause a console script called pbr to be installed that executes the main function found in pbr.cmd. Additionally, two entry points will be installed for pbr.config.drivers, one called plain which maps to the Plain class in pbr.cfg.driver and one called fancy which maps to the Fancy class in pbr.cfg.driver.
0 N- P' l: Z) p8 ?' ~$ T9 ^( ?) z3 A
这段设置会产生一个pbr的脚本,执行pbr.cmd中的main函数,而pbr.config.dirvers则会安装两个进入点,为plain以及fancy。对应Plain,和Fancy函数。
! U3 I) u2 I: c; r+ ~/ J( U' P+ B2 [1 a# I+ ]2 `, q, A
pbr
8 W' K# _' D" E  J) XThe pbr section controls pbr specific options and behaviours.0 U* i( M$ g; K3 _" l' f7 E' t: n5 @

9 \- W" {  w" A, n, ^6 Y  w2 JThe autodoc_tree_index_modules is a boolean option controlling whether pbr should generate an index of modules using sphinx-apidoc. By default, setup.py is excluded. The list of excluded modules can be specified with the autodoc_tree_excludes option. See the sphinx-apidoc man page for more information.
, }  C* b; C: R& u4 ~2 A6 }4 h( X
$ G8 E: ~$ R2 n7 d$ t' EThe autodoc_index_modules is a boolean option controlling whether pbr should itself generates documentation for Python modules of the project. By default, all found Python modules are included; some of them can be excluded by listing them in autodoc_exclude_modules. This list of modules can contains fnmatch style pattern (e.g. myapp.tests.*) to exclude some modules.
/ g4 r2 h: f. j' p; u% m& e& M6 _# s1 ?& \4 t% l
The warnerrors boolean option is used to tell Sphinx builders to treat warnings as errors which will cause sphinx-build to fail if it encounters warnings. This is generally useful to ensure your documentation stays clean once you have a good docs build.
; w" P6 D0 d# e4 K% n$ S" F: D. j. i/ i2 j- z/ H
pbr
7 Q5 p* n# j7 _7 ~, ]% O- t+ c8 z* S& f  ^& E# u( M5 y" v/ b
pbr段落控制pbr相关的参数以及行为
! _/ l5 g0 m8 H% H- K4 l9 v5 o! O/ O  b( W% w9 F( e
autodoct_tree_index_modules 是一个布尔型参数,描述pbr是否应该为sphinx-apidoc中的模块自动产生索引。默认的,setup.py排除在外。可以在autodoc_tree-excludes的设置中设定一系列被排除在外的模块。参考sphinx-apidoc man page 获取更多帮助信息。& r3 G5 v) R8 ?

$ w& Y; f7 K$ C3 j0 O% L7 tautodoc_index_modules是一个布尔型参数,控制pbr是否自己为项目的python模块产生文档。默认的,所以找到的python模块包含在内;他们有些被autodoc_exclude_modules排除掉了。这个排除文件模块列表可以包含fnmatch 风格的设置(例如myapp.tests.*)
- U( U9 J2 F( w. U% G# m1 V7 p& X! u; u# j' c1 V
warnerrors 是一个布尔型参数,用来告诉sphinx 将警告信息看待为出错信息,如果当它遇到警告就会失败。如果你想让你的文档干净良好这样做很必要。
4 J% R: c# X/ e2 Y$ ]1 F' G$ `0 d, d6 Y# c
Note( v: t- P; ]8 L4 v3 t

- T) A5 U$ y: T* |' I/ k2 SWhen using autodoc_tree_excludes or autodoc_index_modules you may also need to set exclude_patterns in your Sphinx configuration file (generally found at doc/source/conf.py in most OpenStack projects) otherwise Sphinx may complain about documents that are not in a toctree. This is especially true if the warnerrors=True option is set. See the Sphinx build configuration file documentation for more information on configuring Sphinx.) z3 W. F0 C! |0 `& a  H4 v

" V- y# R: r9 B* V% q2 E注意: ~: Y3 O6 U9 V4 x9 t
/ f/ _& J9 i. l; {
当使用autodoc_tree_excludes 或者autodoc_index_modules。你也需要在sphinx的配置文件中设置exclude_patterns(一般在项目中的 /doc/source/conf.py找到)。否则sphinx将会告知文档不在toctree中。当warnerrors=True开启后这个也需要为真。参考Sphinx build configuration file 文档获取更多如何配置sphinx.' O* x3 k. ]. `: W/ A3 G1 q
+ \/ h: A) A* n, y5 J

  F+ i/ ]* [9 E# p! o( q" ?4 ?
$ v0 w2 `' [  j1 A5 ~; PComments' _8 I, A) Q" P9 G) c+ T9 N0 O; W
Comments may be used in setup.cfg, however all comments should start with a # and may be on a single line, or in line, with at least one white space character immediately preceding the #. Semicolons are not a supported comment delimiter. For instance:
$ r$ D" v/ Z* k& ^; m
4 K8 b! @8 w+ e) l& Z1 t评论
0 |7 ^' @; D5 B, ]
1 I; U: ^- Z, E( T9 I段落可以设置评论,必须以#开头,#后面紧跟一个空格。: s$ e  m. _( x
; |/ V" Y$ Y2 w% ^9 M5 G
复制代码9 i8 Y9 ]  J" L7 g; A4 s( ~
[section]
# \& w4 y8 p8 W# }% w/ w3 G" I: Z# A comment at the start of a dedicated line) |9 P6 @' ^; S' j1 d
key =5 G: v: @2 W! S. s. B( ?6 K
    value1 # An in line comment
, P( {8 [, }) y6 j    value23 x& H; @4 s9 ~
    # A comment on a dedicated line
, \4 }+ P! u1 R4 N4 h3 Y( D    value3
9 j8 H' g) I) c- Y# i, P7 {复制代码+ j& I3 }1 z* I/ [" s
( x# C' J4 x- r. k" b

- n, I0 x: F7 N5 F* g' x$ m! G5 G4 k
Requirements
# K3 u' X- Z3 {* u4 ~$ QRequirement files should be given one of the below names. This order is also the order that the requirements are tried in (where N is the Python major version number used to install the package):2 e, v& x- r" s; o

7 M6 b2 o+ H+ q2 ?requirements-pyN.txt4 N. `5 `& L* v, z
tools/pip-requires-py35 s9 j4 m: S& \8 {; y0 I
requirements.txt
" o# s/ k; k' U+ y' N  Q/ k0 ctools/pip-requires6 ]$ R) ]: ?) s& C
Only the first file found is used to install the list of packages it contains.5 u, P) ?) |+ C" p
0 i( K$ c) z* _! ~. i' W
Note+ N8 J- y# G0 ^# U1 @! v

' y( I: r$ v+ p) A7 nThe ‘requirements-pyN.txt’ file is deprecated - ‘requirements.txt’ should be universal. You can use Environment markers for this purpose.2 G, A% ]' P* i- {: _
1 a3 H& v# T0 q* u( F4 r( f
项目需求, k1 `  X! g; }6 ^
+ P+ K7 X1 Y7 }. Z* i
需求文档必须以以下名字命名。列举顺序也就是python进行尝试时候的顺序。(这里面N代表python安装包时候的主要版本数字)
  \4 J" B8 v) ]. a: E$ e* T  a& x/ x% _: N. V' {7 F! G& F- N. k* m
requirements-pyN.txt5 C0 S/ m- G- }5 k. E; g
tools/pip-requires-py3
( Q+ x0 B# l+ zrequirements.txt1 w# q, p0 c  Y: L
tools/pip-requires& s: d( P1 B  y7 n$ N
仅仅第一个文件用来安装所有包2 a, C' M5 N" C* d" g# n9 B- d* C$ L
注意9 u% Z7 b+ N8 G% c

1 G& {& @6 ], k) B2 t2 N5 @'requirements-pyN.txt'文件不被建议,而‘requirements.txt’是通用格式,你可以参考Environment markers 查看。8 j$ Z9 O! w9 N* ~5 ^, H
7 B2 ^# c4 a4 f9 E! V3 F  D. T
Extra requirements- U* S5 I% H8 j+ G7 M4 i
Groups of optional dependencies, or “extra” requirements, can be described in your setup.cfg, rather than needing to be added to setup.py. An example (which also demonstrates the use of environment markers) is shown below.5 M) s, a) d& \
: F1 S$ k- n' ]  q. ]9 U' n! d
其他的需求
8 Q6 v" ?4 j8 {1 P# h% c6 h1 a' u: E' I+ R
一组可选的依赖,或者‘额外’的依赖,与其在setup.py中,也可以在setup.cfg中被描述。下面就是一个例子(展示了环境标签的使用)
8 f& B  l" {3 q% A& m3 B5 _" S+ f: y5 P! a7 f
Environment markers( v; ~' D0 q* K3 M9 m, r
Environment markers are conditional dependencies which can be added to the requirements (or to a group of extra requirements) automatically, depending on the environment the installer is running in. They can be added to requirements in the requirements file, or to extras defined in setup.cfg, but the format is slightly different for each.
# s3 b: v  O" D6 p# C& }) x- u% O4 R* y6 l
环境标签9 m& p; t0 N: U$ @5 K
' M, p6 E  C8 Q6 ~4 P+ @+ ?
环境标签是条件依赖,根据运行环境来设定需求文件。它们可以被加入进requirements文档或者在setup.cfg中定义,但是格式有些微不同。
* ]" C/ y5 t5 i! c' s' Y$ A# `2 }1 x% f0 w4 g/ {/ |. d
For requirements.txt:2 ?8 }$ v9 @4 W! c; X  T' G! R$ E
: c9 C# p: Y- B0 }/ p; j
argparse; python_version=='2.6'
1 _; ~) B7 Q! v* E/ t4 X( ~" N- \- q
例如在requirements.txt中& X: E/ \/ p: L+ }( E, A6 e8 s2 K

7 e1 x) R* n+ |argparse; python_version=='2.6'* }( C9 ], f, q
This will result in the package depending on argparse only if it’s being installed into Python 2.6) D- k1 p4 p& U0 F- b

; T9 u& J5 `5 Y: `! K$ QFor extras specifed in setup.cfg, add an extras section. For instance, to create two groups of extra requirements with additional constraints on the environment, you can use:
7 v+ D" H; D" i8 D. o7 a9 a/ F4 q$ s: h- L0 x
这会导致包依赖于argparse包,仅仅当在python2.6的情况下) G5 I- o0 B, F
" F+ R: L, Y- t% O* p! F8 {
而在setup.cfg中,在extras段里面,例如,创建依赖环境的两个需求组,你可以使用以下配置:, b; e; \3 l( @! F0 z+ j& V, h

$ G! t) n6 r: b" i% ~. f复制代码
* f, I# K% s" }; Q' I[extras]
& @: Q+ b6 ?" u5 X+ R7 o( Q# usecurity =
7 l" u% F/ }. x! l/ {4 b    aleph
0 I9 j1 O7 }& @+ S/ S: C' t    bet:python_version=='3.2'! T5 Y& u- \3 d+ r) |0 d
    gimel:python_version=='2.7'
* x0 A, p& T5 K. vtesting =
2 o& e3 h$ h1 \) e: ]9 h    quux:python_version=='2.7'$ f4 U- J) X0 [. X. ?( W8 ~) r
您需要登录后才可以回帖 登录 | 注册

本版积分规则

返回首页|Archiver|手机版|小黑屋|易陆发现技术论坛 ( 蜀ICP备2026014127号-1 )

GMT+8, 2026-6-12 00:01 , Processed in 0.034336 second(s), 23 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表