ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Windows Python开发:Chocolatey + Make + Task三件套实战指南

Windows Python开发:Chocolatey + Make + Task三件套实战指南 上周帮一个刚入职的同事配开发环境他拿着Windows笔记本一脸茫然项目文档里写着make setup、make run但他连make是什么都不知道。装完Python就开跑结果一堆命令敲不下去。我说你先装三样东西chocolatey、make和task。十分钟后他也能像我们一样敲着make run启动项目了。这篇文章把我给同事讲的从头到尾写一遍适合所有在Windows上做Python开发、又想用make和task管理项目的朋友。先说清楚一个容易混淆的点这里的task是Go语言写的那个任务运行器go-task/task不是C#里的Task不是Gradle构建里的task也不是Verilog里的task。这名字撞车严重你在网上搜task 用法很容易搜到一堆无关内容。本文要讲的这三个工具组合起来解决的是一个非常具体的问题让Windows上的Python项目拥有Linux/macOS那样顺滑的命令行工作流。如果你平时只在IDE里点按钮运行代码可能觉得make和task没必要。但一旦项目里涉及虚拟环境创建、依赖安装、启动开发服务器、跑测试、lint、清理缓存这一连串操作你会发现自己每天都在重复敲同样的命令。把这些命令固化成make test或task test效率和规范性都会明显提升。1. 为什么我建议先把这三件套装起来1.1 一个再常见不过的场景被启动Python项目逼疯的下午拿最常见的FastAPI项目举例。你克隆下来代码后首先得创建虚拟环境然后激活它再安装依赖最后跑uvicorn app.main:app --reload。Windows上Bash和PowerShell的激活命令还不一样你每次都得回忆python -m venv .venv .venv\Scripts\activate pip install -r requirements.txt uvicorn app.main:app --reload --port 8000第一天你可能记得住第三天就开始烦了。更麻烦的是团队协作——有人用macOS有人用Windows每个人手动敲命令的方式多少有差异今天少装一个依赖明天忘了带参数。这时候make或task的价值就出来了把步骤固化下来任何人一条命令跑通。但问题在于Windows默认没有make也没有task。你直接敲make会提示未找到命令。于是第一步就是用chocolatey这个Windows包管理器把后面两个工具装上。1.2 三件套各自解决什么问题为什么非它们不可chocolatey的角色类似Homebrew之于macOS或者apt之于Ubuntu。在Windows上装系统级命令行工具Chocolatey算是最省事的方案之一一条命令装完PATH自动配好卸载也干净。当然你也可以用winget或者scoop但我用Chocolatey这么多年在装make和task这类开发工具上它一直很稳。make是GNU的老牌构建工具核心思路是定义一个个target目标每个target下面写要执行的命令文件没变就不重复执行。它原本是给C/C编译用的但用来管理Python项目流程也完全没问题因为make本质上就是一个根据规则执行命令的工具。task则是更现代一点的替代品。同样是定义任务、执行命令但用YAML写配置支持跨平台内置dotenv加载、自动补全、增量构建这些make需要额外折腾才能实现的能力。我在2023年之后新写的项目基本都用task但老项目里保留的Makefile仍然很多所以两个都得会。2. Chocolatey安装实操从零到能敲命令2.1 准备工作PowerShell执行策略与一键安装脚本Chocolatey的安装脚本是PowerShell的默认执行策略可能会拦你。先打开PowerShell不一定要管理员权限但装了之后很多操作需要管理员终端直接执行Set-ExecutionPolicy Bypass -Scope Process -Force [System.Net.ServicePointManager]::SecurityProtocol [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 iex ((New-Object System.Net.WebClient).DownloadString(https://community.chocolatey.org/install.ps1))第一行只对当前进程生效不会永久修改系统的执行策略属于用完即走的干净做法。第二行是把TLS 1.2打开不然某些旧版Windows环境会被服务器拒绝连接。第三行就是从官方脚本地址下载并执行安装。你如果没装任何包管理器建议先装Chocolatey再装Python相关的环境。因为Chocolatey本身支持安装Python但我个人不太建议用choco管理Python版本Python用官方安装包加pyenv-win管理更灵活。Chocolatey在我这里的定位是解决make、task、git、ffmpeg这类系统级命令行工具。2.2 换源默认源太慢时的处理Chocolatey默认源在国外网络环境不好的时候拉包会让人崩溃。装完choco之后你可以先看看当前源列表choco source list我习惯的操作是把默认源移除或者加一个国内镜像源但镜像源的可用性在不同地区、不同时间段不太一样所以我不写死某个地址。你只需要记住一个原则choco source remove --namechocolatey会删掉默认源choco source add --namechocolatey --source替换成你验证过可用的镜像地址 --priority1会加一个新源并设置优先级如果你网速正常其实什么都不用换。choco装make和task的包都很小通常一顿饭的功夫就装完了。真正需要换源的是装大量大型包的时候。2.3 装完之后第一件事验证和常见问题安装完成之后新开一个PowerShell窗口然后敲choco --version正常会输出一个版本号比如2.2.2之类。如果你在原窗口直接敲命令提示找不到大概率是PATH没有刷新重新开一个终端窗口再试。这一步很多人踩坑以为安装失败了其实是Shell缓存了旧的环境变量。如果你在非管理员终端执行choco安装软件会看到类似Permission Denied或者要求管理员权限的错误。Chocolatey在安装包时需要写C:\ProgramData\chocolatey和系统PATH普通权限不够。所以我的习惯是日常开发用普通终端安装软件时右键选择以管理员身份运行PowerShell或Windows Terminal。3. Windows下安装make和task坑比你想的多3.1 用choco装make版本与命令入口的细节在管理员终端执行choco install make -y装完之后新开终端验证make --version这里有几个坑值得单独说说。第一choco源里有个包叫make安装的确实是GNU Make但Windows版GNU Make的默认行为跟Linux上有些微差别最典型的就是shell解析方式。GNU Make在Windows上会优先找sh.exe找不到才退回cmd。如果你后续写Makefile时发现某些命令报错——比如$(shell ...)或rm -rf这种Linux风格命令在Windows下行为异常——很可能就是sh缺失导致的。其次装完make之后建议把安装目录确认一下。choco通常会放到C:\ProgramData\chocolatey\bin\make.exe这个目录已经在PATH里所以直接敲make就能用。如果有人电脑上装了GnuWin32的老版本make可能命令入口冲突建议先把老版本卸载干净再装choco的否则版本错乱排错非常头疼。3.2 用choco装task意外顺利背后的原因task的choco包名就叫task安装同样简单choco install task -y装完验证task --versiontask这个工具本身就主打跨平台官方对Windows支持做得比GNU Make好太多所以安装过程基本没有坑。令我比较舒服的是task还自带了命令行自动补全脚本装完之后执行一次task --completion powershell | Out-String | Invoke-Expression之后每次敲task再按Tab就能看到所有可用任务名。如果你用Windows Terminal加PowerShell这个补全体验相当顺滑。把它写进你的$PROFILE一劳永逸。3.3 装完先用命令行验证别急着关终端装完两个工具后我建议先做一个最简单的冒烟测试建立临时目录写一个十行以内的Makefile和Taskfile.yml各自跑一遍。这个步骤花五分钟但能立刻暴露环境问题而不是等你写完整个项目配置之后再找BUG。比如写一个最简单的Makefilehello: echo hello from make然后写个最简单的Taskfile.ymlversion: 3 tasks: hello: cmds: - echo hello from task跑make hello和task hello两边都输出正确说明环境中make和task都能正常工作。如果make hello报Tab相关错误那就是编辑器把Tab转换成空格了这个也算经典问题。4. 用Makefile启动Python项目最小可用但足够真实的案例4.1 为什么Makefile在Python项目里仍然有一席之地你可能觉得Python项目直接用pip加python -m就够了为什么非要套一层make我的回答是make把你的项目意图写进了文件里。新人克隆项目后不用读README里一大段命令直接make setup就能把环境准备好make test就能跑测试。尤其是CI/CD流水线里Agent跑的就是那几条make命令本地和远端行为完全一致。Makefile的另一个好处是依赖关系。比如run这个目标依赖setup你敲make run时它会先检查setup是否执行过没执行过就先执行setup。这种目标依赖是task也具备的核心能力但make是几十年前就设计好这套机制的老前辈生态里各种项目的Makefile你可以直接抄。4.2 一个可以直接抄的Makefile下面这个Makefile我用了很多项目涵盖虚拟环境、依赖安装、开发服务器、测试、lint、清理这几个最常见的操作PYTHON : python VENV_DIR : .venv VENV_PY : $(VENV_DIR)/Scripts/python.exe .PHONY: setup run test lint clean all help .DEFAULT_GOAL : help all: setup setup: requirements.txt $(PYTHON) -m venv $(VENV_DIR) $(VENV_PY) -m pip install -U pip $(VENV_PY) -m pip install -r $ $(VENV_PY) -m pip install -e . run: setup $(VENV_PY) -m uvicorn app.main:app --reload --port 8000 test: setup $(VENV_PY) -m pytest -v lint: setup $(VENV_PY) -m ruff check . clean: $(VENV_PY) -c import shutil, os; shutil.rmtree($(VENV_DIR), ignore_errorsTrue) help: grep -E ^[a-zA-Z_-]: Makefile | sed s/://注意几个关键点。第一命令前面必须是Tab缩进不能用空格这是Makefile最著名的坑。编辑器如果默认把Tab转成四个空格就会报missing separator错误你需要在编辑器设置里把insert spaces for tabs关掉。第二Windows下虚拟环境的Python解释器路径是.venv/Scripts/python.exe不是Linux/macOS的.venv/bin/python。所以我用VENV_PY变量统一收口换平台时只需要改这一处。第三setup后面跟了requirements.txt意思是这个目标依赖这个文件。如果requirements.txt比上一次运行make时没变化make会提示Nothing to be done for setup跳过安装步骤。这是make增量构建的基本形态。4.3 make命令背后的几个关键行为用的时候你会发现make执行命令的机制其实很有意思。每个target下的每行命令make会分别调用一次shell去执行不是你想象中的一整段脚本一次性跑完。这导致一个坑如果你在某一行里写了cd mydir下一行不会保留这个目录切换。想在一个目录里连续执行要么用cd mydir command的形式要么用反斜杠把行连接起来。另一个经常遇到的行为是make默认会把执行的命令回显到终端你看到一大片输出觉得乱就在命令前加符号让它安静执行。比如python --version终端就只显示结果而不显示命令本身。还有一点Windows下make默认shell可能是cmd.exe也可能是sh.exe取决于环境里有没有sh。你在Makefile里用rm -rf这种命令时cmd里没有rm就会报错。我的做法是跨平台的清理操作一律用python -c执行不依赖 rm 这类Unix命令。上面clean目标就是这么写的。如果你确实喜欢Unix风格命令那就在Windows上装Git Bash并把sh.exe所在目录加到PATH里让make优先用sh解析规则。5. 用task重写一遍Taskfile.yml的现代工作流5.1 Taskfile.yml长什么样同样的功能用task写出来是这样的version: 3 vars: PYTHON: python VENV_DIR: .venv env: PYTHONIOENCODING: utf-8 tasks: default: desc: 显示可用任务 cmds: - task --list setup: desc: 创建虚拟环境并安装依赖 cmds: - {{.PYTHON}} -m venv {{.VENV_DIR}} - {{.VENV_DIR}}/Scripts/python -m pip install -U pip - {{.VENV_DIR}}/Scripts/python -m pip install -r requirements.txt - {{.VENV_DIR}}/Scripts/python -m pip install -e . sources: - requirements.txt generates: - {{.VENV_DIR}} run: desc: 启动开发服务器 deps: - setup cmds: - {{.VENV_DIR}}/Scripts/python -m uvicorn app.main:app --reload --port 8000 test: desc: 运行测试 deps: - setup cmds: - {{.VENV_DIR}}/Scripts/python -m pytest -v lint: desc: 运行代码检查 deps: - setup cmds: - {{.VENV_DIR}}/Scripts/python -m ruff check . clean: desc: 清理虚拟环境 cmds: - {{.VENV_DIR}}/Scripts/python -c \import shutil; shutil.rmtree({{.VENV_DIR}}, ignore_errorsTrue)\task的默认任务叫default你在目录里直接敲task就会执行它输出所有任务的列表。相比Makefile里靠help目标自己解析这个体验友好很多每行任务都有desc描述终端会按列表方式展示。YAML的缩进规则跟Makefile的Tab坑一样需要小心但YAML有个好处缩进错误会直接解析失败不会像Makefile那样报一个missing separator让你琢磨半天。task里引用变量的语法是{{.变量名}}在双引号字符串里使用要注意转义比如clean里用了内层双引号就需要写成\这个细节我第一次写的时候就踩过。5.2 env、dotenv、增量构建这些Makefile没有的能力task内置了env字段可以直接给任务设置环境变量。我在上面设置了PYTHONIOENCODING: utf-8就是为了避免Windows控制台的编码问题导致print中文乱码。这比每次命令行里手动set PYTHONIOENCODINGutf-8优雅得多。task还支持dotenv就是在Taskfile.yml所在目录放一个.env文件task启动时会自动加载里面的变量。比如你的数据库连接串、API密钥这类配置放.env里任务命令里用{{.DATABASE_URL}}引用项目里不用在多个地方硬编码。这一步对Python开发来说非常实在因为pydantic-settings或python-dotenv那一套你平常就在用task只是帮你把启动环节也统一了。增量构建比make更明确。在task里你可以给一个任务声明sources源文件列表和generates产物列表。只有当源文件比产物新时task才会执行命令否则直接显示Task up-to-date跳过。在Makefile里这种基于文件时间戳的增量判断要自己写规则task则开箱即用。5.3 参数传递和子任务编排的实际用法task支持从命令行传参这个能力在Makefile里实现起来比较绕。比如你想指定端口号启动服务run: desc: 启动开发服务器 deps: - setup cmds: - {{.VENV_DIR}}/Scripts/python -m uvicorn app.main:app --reload --port {{.PORT}} vars: PORT: 8000然后命令行执行task run PORT8080不传参数时用默认值8000传了就覆盖。这个模式在多人协作时很实用比如有人机器上8000端口被占了他可以task run PORT9000不用改任何配置文件。多任务编排也顺手。task里有deps和cmds两个概念deps是执行前的依赖任务会在当前任务之前跑完cmds是当前任务真正要做的事。如果你希望并行执行几个独立的检查任务task还支持在cmds里给每条命令加ignore_error之类的标记处理部分失败场景比make灵活。6. make和task在真实项目中怎么选、怎么共存6.1 直接对比一张表看清差别维度GNU maketask配置文件MakefileTaskfile.yml配置语法自定义DSLTab敏感YAML结构清晰跨平台能力依赖shellWindows需额外注意原生跨平台内置Windows支持增量构建基于文件时间戳的target规则sources/generates声明式判断环境变量需自己设或从系统环境读取env字段加dotenv加载自动补全需外部脚本自带一条命令生成参数传递需用$(MAKECMDGOALS)或环境变量绕直接支持task 任务名 参数值学习成本老牌资料多但语法怪YAML几乎零成本上手生态成熟度极高到处都能见到Makefile新兴但在快速增长表格看下来make file默认会找哪些文件GNU make按顺序找GNUmakefile、makefile、Makefile。很多人以为只有Makefile其实另外两个名字也认。所以当你看到这个错误时第一反应应该是当前目录里是不是根本没有这三个文件还是文件名写错了比如把文件命名成了makefile.txtmake当然找不到。有一种情况经常误导人你明明有Makefile但make还是报这个错。这时候通常是你所在目录不对或者Makefile文件名大小写不一致。Linux下文件系统对大小写敏感Makefile和makefile是不同文件如果你在Linux环境建了一个叫makefile的文件但是Makefile的内容里又约定别的target名那运行起来也会有诡异的错位。再比如热词里的error writing temporary file make这个其实是make在运行时无法写入临时文件导致的。通常跟两个因素有关一是TMP/TEMP环境变量指向的目录不存在或者没有写权限二是磁盘空间满了。排查思路就两步确认临时目录存在且可写清理一下磁盘空间基本能解决。还有一类make报错非常容易误导新手比如[makefile:18: libs] Error 1或者common.mk:82: *** kernel header files not in any。这类报错看起来是makefile第几行出了问题但真实的错误往往出在该行命令内部——比如编译某个库失败、头文件路径不对、权限不够。make只是把你的命令执行结果中的非零退出码原样上报了。我的排查习惯是看到这种带Error N的报错先不急着改makefile而是去跑一下出错的原始命令看它自己报什么错往往问题就清楚了。这也引出一个更基础的经验make报错显示的行号指的是规则所在的行不是命令失败的原因。命令本身的问题要靠命令输出来判断。比如你看到make: .//incdefs.sh: Permission denied这是shell脚本没有可执行权限跟makefile写得对不对毫无关系。chmod x incdefs.sh就解决了。这类问题在你从Linux服务器复制项目到Windows再跑交叉编译时特别常见。6.3 我的选型建议和混合使用的场景讲完对比和排错说说我在真实项目里的决策逻辑。如果项目已经存在成熟的Makefile而且团队同事都在用我不会为了追求新工具而强行迁移。工具是服务效率的不是用来折腾同事的。但如果是新项目我基本默认用task。理由是新项目的Taskfile.yml更短、更容易写对Windows和macOS同事的体验差异更小CI里也能直接用同一套配置文件。还有一个很实用的混合玩法用task做本地开发入口用Makefile做CI的稳定入口。也就是Taskfile.yml里定义所有开发任务Makefile里保留几个关键target但实际调用的命令是task run.PHONY: run run: task run这样本地开发的人可以享受task的补全和dotenvCI脚本不用改继续用make调用已经验证过的入口。我在几个微服务项目里用过这种做法团队反馈很好因为新同事日常只需要接触Taskfile.yml旧CI配置完全不动。如果你两种配置文件都要维护我建议把公用的环境变量和路径信息抽到.env和.config里两边都去读避免一份改动另一份忘记同步。task读.env是自动的make那边需要你在Makefile开头加一句include .env配合export才能读到变量稍微麻烦但值得做。工具说到底只是手段。我见过不少团队每天花大量时间在启动项目的姿势上内耗却没有把命令固化到一个文件里。花半小时装好chocolatey、make和task再花半小时把常用命令写进配置之后每一天的开发都会轻松很多。
返回列表