`
bcyy
  • 浏览: 1881062 次
文章分类
社区版块
存档分类
最新评论

py2exe+inno setup集成打包python程序

 
阅读更多

版权所有,转载请注明出处:http://guangboo.org/2013/03/16/build-package-with-py2exe-inno-setup

在使用python开发windows程序时,我们都会对程序进行打包,而对于使用python语言编写的windows程序,包括窗体程序和控制台程序,通常使用py2exepyinstaller来进行打包。由于我没有使用过pyinstaller,因此本文所使用的打包库或工具是py2exe。然而,这个打包过程只是将诸多python文件打包成一个.exe文件,并且将python运行环境,及必要的python库一并打包,这样的打包过程会生成一个.exe文件(默认情况还会有一个w9xpopen.exe文件,该文件是为win98系统使用的),和python27.dl(根据所使用Python版本,文件名会有所不同),很多.pyd文件,及其他文件。这样打包的结果不是我们想象中的windows下的安装文件,本文结合使用py2exe和inno setup,对Python编写的windows程序打包成安装文件。

py2exe打包

由于本文示例代码是基于Python 2.7版本开发的,因此需要将VC2008的运行时打包进去,因为python 2.7是基于VC 2008编译的,可以参考:http://www.py2exe.org/index.cgi/Tutorial#Step521。本文示例打包的程序是使用wxPython开发的windows窗体程序,因此,我们的打包需要将wx库添加进来,并且程序不考虑windows98系统,因此希望把w9xponen.exe文件排除掉,并希望将python基础的类库打包进一个shared.zip文件中。setup.py示例代码如下:

import sys
import os
from glob import glob
from distutils.core import setup
import py2exe
import app

includes = ['wx', 'wx.html', 'select', 'hashlib']
excludes = ['bz2', 'unicodedata']
packages = []
dll_excludes = ['w9xpopen.exe']
VERSION = app.VERSION
cw = os.path.dirname(__file__)

data_files = [('', ['License.txt']),
              ('', [os.path.join(cw, 'icons/app.ico')]),
              ('lib', glob(r'C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT\*.*'))]

sys.path.append("C:\\Program Files\\Microsoft Visual Studio 9.0\\VC\\redist\\x86\\Microsoft.VC90.CRT")

try:
    build_file = open('../dist/build.txt', 'r')
    build = int(build_file.readline())
except:
    build = 0

setup(
    version = VERSION+ '.' + str(build),
    name = 'test app',
    description = 'app for test.',
    long_description = '',
    author = 'Jeff Zhang',
    author_email = 'guangboo49@gmail.com',
    data_files = data_files,
    options = {'py2exe':{'compressed':1,
                         'optimize':2,
                         'includes':includes,
                         'excludes':excludes,
                         'packages':packages,
                         'dll_excludes':dll_excludes,
                         'bundle_files':3,
                         'dist_dir':os.path.join('../dist/',VERSION),
                         'xref':False,
                         'skip_archive':False,
                         'ascii':False,
                         'custom_boot_script':'',
                         }
               },
    zipfile = 'lib/shared.zip',
    windows = [{'script':'app.py',
                'icon_resources':[(1, 'icons\\app.ico')],
                'copyright':'guangboo49@gmail.com',
                'company_name':'Jeff zhang',
                'name':'test name',
                'version':VERSION + '.' + str(build)}],
)

open('../dist/build.txt', 'w').write(str(build + 1))

示例中,build是用来作为版本号的最后一位,表示生成的编号,该编号一直往上涨,不予主版本和次版本号的变化而清零。build版本号保存在一个build.txt文件中,每次执行该脚本时都会提前文件中的数字作为build版本号,打包完后,将加一后的新build版本号再保存到build.txt文件中。setup函数制定了一个zipfile参数,其值为lib/shared.zip,表示将Python基础库打包到lib/shared.zip文件中。另外需要注意到是windows参数中icon_resources,因为该参数涉及到的ico文件如果设置不正确,可能在windows 7等系统中显示出现问题,可以参考,之前专门的文章提供的解决方案:http://guangboo.org/2013/01/10/exe-file-packaging-with-py2exe-cant-display-ico-in-vista-win7

以上脚本是将python程序打包成.exe可执行文件(包括其他dll和pyd等文件),上面的脚本生成的结果,如下图目录结构:

inno setup打包

windows下有很多打包工具,如setup factory, inno setup等,这两种方式我都使用过,但是inno setup相对根据容易一下,只需要一个.iss文件即可实现打包过程,并且.iss文件的格式和.ini文件非常相似,并且可以在.iss文件中直接编写打包逻辑,其编写脚本是使用Pascal语言进行编写的。如下为简单的打包脚本:

[Setup]
AppId={{75BB8432-50eD-436A-873E-844CDC495B22}
AppName=test app
AppVersion=0.17.11
AppVerName=test app(0.17.11)
VersionInfoDescription=XXX Co.,Ltd.
VersionInfoProductName=test app
VersionInfoProductVersion=0.17.11
VersionInfoVersion=0.17.11
VersionInfoTextVersion=0.17.11 Alpha
VersionInfoCompany=XXX Co.,Ltd.
VersionInfoCopyright=Copyright (C) XXX Co.,Ltd. All right reserved.
AppPublisher=XXX Co.,Ltd.
AppCopyright=Copyright (C) XXX Co.,Ltd. All right reserved.
AppPublisherURL=http://www.xxxx.net
DefaultDirName={pf}\test app
DefaultGroupName=test app
LicenseFile=
OutputDir=D:\setup\0.17.11
SetupIconFile=D:\src\app.ico
OutputBaseFilename=setup-0.17.11
Compression=lzma
SolidCompression=yes

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: desktopicon; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkablealone
Name: quicklaunchicon; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "../vcredist_x86.exe"; DestDir:"{tmp}"; Check:NeedInstallVC9
Source: "D:\dist\0.17.11\app.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\dist\0.17.11\python27.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\dist\0.17.11\app.ico"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\dist\0.17.11\lib\*"; DestDir: "{app}/lib"; Flags: ignoreversion recursesubdirs createallsubdirs

[Icons]
Name: "{group}\test app"; Filename: "{app}\app.exe"
Name: "{group}\{cm:ProgramOnTheWeb, test app}"; Filename: "http://www.xxxx.net"
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\test app"; Filename: "{app}\app.exe"; Tasks: quicklaunchicon

[Run]
Filename: "{tmp}/vcredist_x86.exe"; Parameters: /q; WorkingDir: {tmp}; Flags: skipifdoesntexist; StatusMsg: "Installing Microsoft Visual C++ Runtime ..."; Check: NeedInstallVC9
Filename: "{app}\app.exe"; Description: "{cm:LaunchProgram,app.exe}"; Flags: nowait postinstall skipifsilent

[Code]
var vc9Missing: Boolean;
 
function NeedInstallVC9(): Boolean;
begin
  Result := vc9Missing;
end;
 
function InitializeSetup(): Boolean;
var version: Cardinal;
begin
  if RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{FF66E9F6-83E7-3A3E-AF14-8DE9A809A6A4}', 'Version', version) = false then begin
    vc9Missing := true;
  end;
  result := true;
end;

其中[Code]节定义了一个函数,用于判断是否需要安装VC9运行时。因为我在使用inno setup打包时也把VC9运行时打包进去了,如果客户端没有安装VC9运行时的话,inno setup就会调用VC9的安装包vcredist_x86.exe进行安装。[Files]节表示要打包的文件,第一行包含了vcredist_x86.exe文件。

版本控制

因为我们每次打包时可能会有python代码的修改,有代码修改就有版本的变化,前面py2exe打包时,我们使用build版本号,来表示生成或打包的变化。然而这个版本号一般不是主要的,主要的变化在于主版本和次版本,有时还包括修正版本。为了区别和记录软件打包的版本变化,本文主要区分主版本,次版本和修正版本,当版本号的这三个部分发生变化时,都将生成新的目录,如0.7.11表示0.7.11版本打包的目录,0.7.12表示下一个修正版本的目录。

由于我们采用的是先使用py2exe进行打包生成可执行文件,然后在使用inno setup进行打包生成安装文件。并且py2exe输出目录在dist目录,inno setup的输出目录是setup目录,目录结构如图:

另外,py2exe的版本我们可以通过变量VERSION来定义,在inno setup脚本中,我们发现版本号是写死的,这样对于inno setup打包就比较麻烦,每次版本变化就要编写一个新的脚本文件,虽然内容只有版本号不同。因此我们也希望能有一个变量,像py2exe打包一样有一个VERSION变量来定义。

py2exe, inno setup是否可以集成

一方面由于版本的变化会带来inno setup打包要编写新的脚本文件的麻烦,另一方面,这两个打包过程还是分开的,打包必须先执行setup脚本,使用py2exe生成可执行文件,然后在编写iss脚本,使用inno setup compiler来打包成安装文件。这个的过程本身也很麻烦,每次版本的变化都要这么麻烦的打包过程,可能觉得麻烦。理想的方案就是,执行一个setup.py脚本即可将两个打包过程都完成。

其实这个过程也不难实现,因为py2exe库提供了一个接口,允许我们添加自己的打包过程,或在打包完成后执行自定义的功能。我们的需求就是在py2exe打包完成后,自动根据当前版本号生成iss脚本,并且使用inno setup compiler来执行新生成的iss脚本文件。py2exe提供了这样的接口:

from py2exe.build_exe import py2exe
from distutils.dir_util import remove_tree

class build_installer(py2exe):
    # This class first builds the exe file(s), then creates a Windows installer.
    # You need InnoSetup for it.
    def run(self):
        # First, let py2exe do it's work.
        py2exe.run(self)
        # your own business codes.

上面的代码是定义了一个新的打包过程的类,继承了默认的py2exe类,我们可以在py2exe.run(self)代码后添加自己的代码,如删除py2exe打包过程中生成的build临时目录,根据当前版本生成iss脚本,并调用inno setup compiler执行该脚本,生成安装文件。

py2exe提供了扩展接口,那么现在的问题就是inno setup compiler进程的是否支持启动参数呢,即是否可以传递一个iss文件地址给该进程执行。当然有,inno setup compiler支持这样的参数,其格式是:

compil32.exe /cc 'iss file name'

现在问题都得到答案了,py2exe和inno setup两个打包过程是可以集成的,那么下面就是怎么集成的问题了。

自动生成ISS文件

集成的方案已经定下来了,并且可能遇到的问题也已经有了方案。下面还有一个工作没有做,就是根据当前版本生成iss脚本文件的过程。这个过程其实简单,因为有了上面的脚本示例,每次生成的脚本只有版本号不同而已,其他都可以直接输出。需要注意与版本相关的目录名和文件名。如下给出的示例代码:

# -*- coding:utf-8 -*-

import app
import os

class InnoScript:
    def __init__(self, output, input):
        self._output = os.path.join(output, app.VERSION_TEXT)
        self._input = input
        self._script_file = os.path.join(self._output, 'setupscript-%s.iss' % app.VERSION_TEXT)
        self._exename = 'app.exe'

    def _create_script_file(self):
        scf = os.path.dirname(self._script_file)
        exec_path = os.path.abspath(os.path.dirname(__file__))
        
        if not os.path.exists(scf):
            os.mkdir(scf)
            
        f = open(self._script_file, 'w')
        print >> f, "[Setup]"
        print >> f, "AppId={{75BB853E-503D-416A-873E-844CDBB95B22}"
        print >> f, "AppName=%s" % app.NAME
        print >> f, "AppVersion=%s" % app.VERSION_TEXT
        print >> f, "AppVerName=%s(%s)" % (app.NAME, app.VERSION_TEXT)

        print >> f, "VersionInfoDescription=%s" % app.COMPANY
        print >> f, "VersionInfoProductName=%s" % app.NAME
        print >> f, "VersionInfoProductVersion=%s" % app.VERSION_TEXT
        print >> f, "VersionInfoVersion=%s" % app.VERSION_TEXT
        print >> f, "VersionInfoTextVersion=%s %s" % (app.VERSION_TEXT, 'Alpha')
        print >> f, "VersionInfoCompany=%s" % app.COMPANY
        print >> f, "VersionInfoCopyright=%s" % app.COPYRIGHT
        
        print >> f, "AppPublisher=%s" % app.COMPANY
        print >> f, "AppCopyright=%s" % app.COPYRIGHT
        print >> f, "AppPublisherURL=%s" % app.COMPANY_SITE
        print >> f, "DefaultDirName={pf}\XXX/test app"
        print >> f, "DefaultGroupName=%s" % app.NAME
        print >> f, "LicenseFile=%s" % app.LICENSE_FILE
        print >> f, "OutputDir=%s" % self._output
        print >> f, "SetupIconFile=%s" % os.path.join(exec_path, 'app.ico')
        print >> f, "OutputBaseFilename=setup-%s" % app.VERSION_TEXT
        print >> f, "Compression=lzma"
        print >> f, "SolidCompression=yes"
        print >> f, ""
        print >> f, "[Languages]"
        print >> f, 'Name: "english"; MessagesFile: "compiler:Default.isl"'
        print >> f, ""
        print >> f, "[Tasks]"
        print >> f, 'Name: desktopicon; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkablealone'
        print >> f, 'Name: quicklaunchicon; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked'
        print >> f, ""
        print >> f, "[Files]"
        print >> f, 'Source: "../vcredist_x86.exe"; DestDir:"{tmp}"; Check:NeedInstallVC9'
        print >> f, 'Source: "%s\%s"; DestDir: "{app}"; Flags: ignoreversion' % (self._input, self._exename)
        print >> f, 'Source: "%s\python27.dll"; DestDir: "{app}"; Flags: ignoreversion' % self._input
        print >> f, 'Source: "%s\app.ico"; DestDir: "{app}"; Flags: ignoreversion' % self._input
        print >> f, 'Source: "%s\lib\*"; DestDir: "{app}/lib"; Flags: ignoreversion recursesubdirs createallsubdirs' % self._input
        print >> f, ""
        print >> f, "[Icons]"
        print >> f, 'Name: "{group}\%s"; Filename: "{app}\app.exe"' % app.NAME
        print >> f, 'Name: "{group}\{cm:ProgramOnTheWeb,%s}"; Filename: "%s"' % (app.NAME, app.COMPANY_SITE)
        print >> f, 'Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\%s"; Filename: "{app}\%s"; Tasks: quicklaunchicon' % (app.NAME, self._exename)
        print >> f, ""
        print >> f, "[Run]"
        print >> f, 'Filename: "{tmp}/vcredist_x86.exe"; Parameters: /q; WorkingDir: {tmp}; Flags: skipifdoesntexist; StatusMsg: "Installing Microsoft Visual C++ Runtime ..."; Check: NeedInstallVC9'
        print >> f, '''Filename: "{app}\%s"; Description: "{cm:LaunchProgram,%s}"; Flags: nowait postinstall skipifsilent''' % (self._exename, self._exename)
        print >> f, ""
        print >> f, '''[Code]
var vc9Missing: Boolean;
 
function NeedInstallVC9(): Boolean;
begin
  Result := vc9Missing;
end;
 
function InitializeSetup(): Boolean;
var version: Cardinal;
begin
  if RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{FF66E9F6-83E7-3A3E-AF14-8DE9A809A6A4}', 'Version', version) = false then begin
    vc9Missing := true;
  end;
  result := true;
end;'''
        f.close()
        
    def compile(self):
        self._create_script_file()
        import subprocess
        proc = subprocess.Popen('compil32.exe /cc "%s"' % self._script_file)
        proc.wait()

上面的代码中有一个app模块,该模块定义了一些常量,主要有COMPANY, NAME等。该类的compile方法用调用了_create_script_file来生成iss脚本文件,然后调用inno setup的编译器compil32.exe,来编译刚生成的iss脚本。

打包集成

现在一切准备工作都做好了,下面一步就是将这些过程集成起来,使一次执行可以生成可执行文件和安装文件。前面介绍了py2exe的扩展接口,下面我们就将自定义的代码添加进去,代码如下:

from py2exe.build_exe import py2exe
from distutils.dir_util import remove_tree
class build_installer(py2exe):
    # This class first builds the exe file(s), then creates a Windows installer.
    # You need InnoSetup for it.
    def run(self):
        # First, let py2exe do it's work.
        py2exe.run(self)
        remove_tree(os.path.join(cw, 'build'))
        setup_dir = os.path.join(self.dist_dir, '../../setup')
        import inno_script
        script = inno_script.InnoScript(setup_dir, self.dist_dir)
        script.compile()

相比之前的代码,我们添加了删除py2exe打包过程生成的build临时目录的代码和生成安装文件的代码(包含生成iss脚本和编译两个过程)。然而只有新的py2exe类的定义还不够,需要在setup方法中使用它才行。使用方法也非常简单,只要在setup方法中添加一个cmdclass参数,其值为{"py2exe": build_installer},即可。这里只贴出setup方法的后半部分代码,前半部分和之前一样:

setup(
    ...,
    windows = [{'script':'app.py',
                'icon_resources':[(1, 'icons\\app.ico')],
                'copyright':'guangboo49@gmail.com',
                'company_name':'Jeff zhang',
                'name':'test name',
                'version':VERSION + '.' + str(build)}],
    cmdclass = {"py2exe": build_installer},
)

另外,如果找不到compil32.exe命令时,请确认正确安装了inno setup,并且将inno setup根目录添加到PATH环境变量中。

总结

本文是为在使用python编写windows程序时,对繁琐的打包过程提出的一种解决方案,以便简化打包过程,使专注于程序开发,而不是打包。本文中的打包过程与版本管理有一定的关联,本文中的版本号保存在app.py模块,VERSION常量中,如版本发生变化,可以修改该值,然后重新执行setup.py脚本。

分享到:
评论

相关推荐

    python-innosetup:distutils扩展模块-通过InnoSetup创建安装程序

    一个Python innosetup库 要求 Python 2.5或更高版本 特征 您可以使用自定义的InnoSetup脚本。 通过setup()元数据的安装程序元数据 从setup()元数据生成AppId(GUID)请...import py2exe, innosetup # All optio

    JavaFX+SpringBoot2.7+JDK17+Launch4j+InnoSetup实现JVMTI的Jar包加密/H2数据

    javaFX的界面框架有很多,实际业务中我们常常要将界面框架与整体业务框架融为一体,bookmanager将...本例中还提供了build.py一键编译的脚本文件,改脚本需python3环境,通过该脚本可一键编译并输出windows安装程序。

    Python程序打包成windows安装文件资源

    本篇将详细讲解如何使用Python2.7、py2exe、PyInstaller-2.1以及Inno Setup Compiler将Python程序打包成Windows安装文件。 首先,你需要确保你的开发环境是Python2.7。虽然现在Python3已经广泛使用,但某些库或工具...

    py2exe入门指南

    - **定义与功能**:`py2exe`是一种工具,能够将基于Python语言编写的程序转换为Windows平台上的`.exe`可执行文件。这意味着用户可以在没有安装Python解释器的环境下运行这些程序。 - **适用范围**:特别适合于那些...

    软件打包工具 打包 工具 各种语言打包工具

    - **Python**: 对于Python项目,常见的打包工具有`setup.py`与`pip`,它们可以创建`.whl`和`.tar.gz`格式的源码包,以及`.exe`(通过PyInstaller或cx_Freeze)和`.msi`(通过py2exe)等可执行文件。 - **Java**:...

    Python_应用发布技术1

    它可以自动化处理将Python程序与数据文件一起打包的过程,并且可以调用InnoSetup创建安装程序。然而,它有时会打包不必要的文件,导致打包后的程序体积过大。 3. **Installer**(PyInstaller):原先是商业软件,但...

    Visual C++项目jenkins的通用配置解决方案用到的脚本

    执行innosetup打包 ``` echo %WORKSPACE% cd /d C:\Program Files (x86)\Inno Setup 5 iscc /Qp "%WORKSPACE%\builder\GenerateZip.iss" ``` 执行rc资源文件版本号加一 ``` echo begin_Update_Version python %...

    程序打包工具

    - **Python**:PyInstaller、cx_Freeze、py2exe等。 - **.NET**:ClickOnce、WiX Toolset、NSIS (Nullsoft Scriptable Install System)等。 - **Node.js**:npm pack、Webpack、Electron Builder等。 - **C/C++*...

    windows-installer:Windows的Software Carpentry安装程序

    可以使用pip安装py2exe并可以使用的自动安装包安装Inno Setup。 python setup.py install python setup.py py2exe ISCC.exe swc-installer.iss 对于不想构建自己版本的人,也最新版本。 使用 只需让您的学生下载...

    使用 Python 开发 Windows桌面程序-综合文档

    5. 发布与部署:使用安装包制作工具(如Inno Setup、py2exe、cx_Freeze等)将Python程序打包为可执行文件,方便用户安装和使用。 总的来说,Python为Windows桌面程序开发提供了多样化的选择,无论是初学者还是经验...

    FileLetternew2.rar_日记_日记本

    7. **发布和打包**:为了分发给他人使用,开发者可能使用了如Inno Setup或py2exe这样的工具将程序打包成可执行文件。 8. **软件测试**:在发布前,对软件进行单元测试和集成测试以确保其功能正常且无明显错误。 总...

    软件、项目打包

    - **Python项目**:Python有setup.py文件,通过pip或conda可以打包为轮子(.whl)文件,方便分发和安装。 - **Java项目**:Maven和Gradle可以将Java项目打包成JAR、WAR或EAR文件,适用于不同部署环境。 3. **中文...

    Python ANT project-开源

    Inno Setup是一个免费的安装制作软件,可以用来打包Python应用程序,以便用户方便地进行安装。 5. `setup.py`:这是Python项目的标准配置文件,用于描述项目信息、设定安装选项,并执行安装过程。用户可以通过运行 ...

    生成安装文件

    打包工具有很多种,例如Java领域的JAR/WAR打包、.NET的MSI安装包、Python的pip或setup.py等。 部署则是将打包后的软件安装到目标环境(如用户的计算机、服务器或云端)上,使其可供用户使用。部署过程中可能涉及...

    三子棋(初次完成版)

    这涉及软件打包技术,如Python的py2exe,Java的JAR打包,或是更复杂的安装构建工具如Inno Setup。 7. **版本控制**:尽管没有明确提及,但作为项目管理的好习惯,初学者应学习使用Git等版本控制系统来管理代码的...

    制作产品的安装包

    对于Python项目,可能涉及pipenv或setup.py。确保所有依赖项被正确地管理和打包,以便在目标环境中顺利运行。 "工具"在制作安装包过程中扮演着重要角色。有许多工具可以帮助我们自动化这个过程: 1. **Inno Setup*...

    HeWIT-开源

    4. "setup.py"通常用于Python项目的构建和安装,它会根据Python的打包规范将源代码打包成可执行程序或者安装包。 5. "README.txt"是项目的基本说明文件,通常包含项目介绍、安装指南、使用方法和贡献者信息等。 6. ...

    SourcePreviewHandler.64bit.patched

    Inno Setup (.iss) Java (.java) JavaScript (.js) JavaServer Pages (.jsp) Lisp (.lsp, .lisp) Makefile (.mak) Matlab (.m) NFO (.nfo) NSIS (.nsi, .nsh) Pascal (.pas, .inc) Perl (.pl, .pm, .plx) ...

    ACS-Installer-源码.rar

    "ACS-Installer"的源码可能是用这些常见语言之一,或者是专门用于创建安装程序的脚本语言,如NSIS(Nullsoft Scriptable Install System)或Inno Setup。 3. **类和函数**:源码中会包含各种类定义和函数实现,它们...

    模拟题2_更新版本.rar

    如果包含的是软件安装包,可能涉及到软件打包技术,如NSIS、Inno Setup等。 若要深入讨论具体知识点,需要更详细的信息。如果你能提供压缩包内的具体文件类型或内容描述,我将能提供更加精确和详尽的知识点介绍。

Global site tag (gtag.js) - Google Analytics