Mercurial에서 공유 종속성이있는 프로젝트를 구성하는 좋은 방법은 무엇입니까?
현재 저는 레거시 버전 관리 시스템에서 그룹의 프로젝트를 수은으로 옮기고 있습니다. 이동중인 코드 종류의 한 예로, 공통 코드에 의존하는 여러 개별 응용 프로그램 영역을 포함하는 25 개 이상의 프로젝트 Visual Studio 솔루션이 있습니다. Stack Overflow를 살펴보면 가장 가까운 질문은 this 였지만 버전 제어에 대해서만 언급했습니다. Mercurial을 사용하여 이러한 종속성을 관리하는 특정 구현 기술에 대한 추가 조언을 찾고 있습니다.
종속성의 단순화 된보기는 다음과 같습니다. (이것은 설명과 예를위한 것입니다. 실제 종속성은 훨씬 더 복잡하지만 본질적으로 유사합니다.)
Common Lib 1
/ | \
---- | -----
/ | \ \
App 1 Common Lib 2 \ App 2
/ | \ \
------- | ------ |
/ | \|
App 3 App 4 App 5
Common Lib 모듈은 공유 코드가 될 것입니다. 이것은 DLL 또는 SO 또는 컴파일과 런타임 모두에서 동시에 모든 앱간에 사용되는 다른 라이브러리입니다. 그렇지 않으면 응용 프로그램이 서로 독립적으로 실행될 수 있습니다.
수은 저장소를 설정하는 데 몇 가지 목표가 있습니다.
- 중요한 각 응용 프로그램 또는 구성 요소 그룹에 자체 저장소를 제공합니다.
- 각 저장소를 자체 포함되도록합니다.
- 프로젝트의 합계를 자체 포함하십시오.
- 전체 코드베이스를 한 번에 쉽게 빌드 할 수 있습니다. (결국 이러한 모든 프로그램과 라이브러리는 단일 설치 프로그램으로 끝납니다.)
- 단순하게 유지하십시오.
한 가지 다른 점은 이러한 각 프로젝트에 대해 별도의 저장소가있는 서버가 설정되어 있다는 것입니다.
이 프로젝트를 배치하는 몇 가지 방법을 봅니다.
1. 모든 것을 포함하는 "Shell"저장소를 만듭니다.
이것은 URL 기반 하위 저장소를 사용합니다 (예 : .hgsub에서 .App1 = https://my.server/repo/app1
+---------------------------+
| Main Repository |
| | +---------------------+ |
| +-| Build | |
| | +---------------------+ |
| | +---------------------+ |
| +-| Common Lib 1 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| Common Lib 2 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 1 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 2 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 3 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 4 | |
| | +---------------------+ |
| | +---------------------+ |
| +-| App 5 | |
| +---------------------+ |
+---------------------------+
셸 저장소의 각 기본 폴더에는 각 프로젝트 영역에 대해 하나씩 하위 저장소가 포함됩니다. 종속성은 상대적입니다. 예를 들어 App 4에는 Common Lib 2가 필요하므로 단순히 상대 경로를 사용하여 해당 공통 라이브러리를 참조합니다.
이 접근 방식의 장점 :
- 각 라이브러리는 한 번만 풀다운됩니다.
- Mercurial의 하위 저장소는 해당 하위 저장소의 한 버전 만 프로젝트에 존재하기 때문에 모든 프로젝트에서 동일한 버전의 라이브러리가 자동으로 사용되도록합니다.
- 각 리소스를 쉽게 찾을 수 있습니다.
이 접근 방식의 단점 :
- 앱에서 독립적으로 작업 할 수 없습니다. 예를 들어 내가 App 2에서 작업하고 공통 라이브러리를 변경해야하는 경우 다른 모든 앱에서 지금 바로 이러한 변경 사항을 적용해야합니다.
- 앱 리포지토리를 자체적으로 가져 오는 경우 빌드하려면 직접 필요한 다른 종속 리포지토리를 파악 (또는 알고 있어야합니다).
- 종속성은 강력하게 분리되지 않습니다. 모든 기능을 쉽게 얻을 수 있기 때문에 새 기능을 어디에 든 삽입하고 싶을 것입니다.
2. 종속 하위 저장소가 완전히 포함되도록합니다.
이 접근 방식에서 각 응용 프로그램은 이전과 같이 자체 저장소를 갖지만 이번에는 자체 소스에 대해 하나, 각 종속 하위 저장소에 대해 하나의 하위 저장소도 포함합니다. 그러면 전체 저장소에 이러한 각 프로젝트 저장소가 포함되고 전체 솔루션을 빌드하는 방법을 알 수 있습니다. 이것은 다음과 같습니다.
+-----------------------------------------------------------------------+
| Main Repository |
| +--------------------+ +--------------------+ +--------------------+ |
| | Build | | Common Lib 1 | | Common Lib 2 | |
| +--------------------+ | | +--------------+ | | | +--------------+ | |
| | +-| Lib 1 Source | | | +-| Common Lib 1 | | |
| | +--------------+ | | | +--------------+ | |
| | | | | +--------------+ | |
| | | | +-| Lib 2 Source | | |
| | | | +--------------+ | |
| +--------------------+ +--------------------+ |
| +--------------------+ +--------------------+ +---------------------+ |
| | App 1 | | App 2 | | App 3 | |
| | | +--------------+ | | | +--------------+ | | | +--------------+ | |
| | +-| Common Lib 1 | | | +-| Common Lib 1 | | | +-| Common Lib 2 | | |
| | | +--------------+ | | | +--------------+ | | | +--------------+ | |
| | | +--------------+ | | | +--------------+ | | | +--------------+ | |
| | +-| App 1 Source | | | +-| App 2 Source | | | +-| App 3 Source | | |
| | +--------------+ | | +--------------+ | | +--------------+ | |
| +--------------------+ +--------------------+ +---------------------+ |
| +--------------------+ +--------------------+ |
| | App 4 | | App 5 | |
| | | +--------------+ | | | +--------------+ | |
| | +-| Common Lib 2 | | | +-| Common Lib 1 | | |
| | | +--------------+ | | | +--------------+ | |
| | | +--------------+ | | | +--------------+ | |
| | +-| App 4 Source | | | +-| Common Lib 2 | | |
| | +--------------+ | | | +--------------+ | |
| +--------------------+ + | +--------------+ | |
| | +-| App 5 Source | | |
| | +--------------+ | |
| +--------------------+ |
+-----------------------------------------------------------------------+
장점 :
- 각 응용 프로그램은 서로 독립적으로 자체적으로 구축 할 수 있습니다.
- Dependent versions of libraries can be tracked per-app, instead of globally. It takes an explicit act of inserting a subrepo into the project to add a new dependency.
Cons:
- When doing the final build, each app might be using a different version of a shared library. (might need to write tools to sync the common lib subrepos. Eww.)
- If I want to build the entire source, I end up pulling down shared libraries multiple times. In the case of Common Lib 1, I would have to pull it eight (!) times.
3. Don't include dependencies at all as subrepos - bring them in as part of the build.
This approach would look much like approach 1, except the common libraries would only be pulled as part of the build. Each app would know what repos it needed, and put them in the common location.
Pros:
- Each app could build by itself.
- Common libraries would only need to be pulled once.
Cons:
- We'd have to keep track of versions of libraries currently used by each app. This duplicates subrepo features.
- We'd have to build an infrastructure to support this, which means more stuff going into build scripts. Ugh.
4. What else?
Is there another way of handling it? A better way? What ways have you tried and succeeded, what ways have you tried but hated? I'm currently leaning towards 1, but the lack of application independence, when it should be able to, really bothers me. Is there a way to get the nice separation of method 2 without the massive duplicate code pull and dependency maintenance nightmare, while not having to write scripts to handle it (like in option 3)?
Dependencies management is an important aspect of a project's organization, to my eyes. You exposed in great details various solutions, based on the subrepos feature of Mercurial, and I agree with all the pros/cons that you gave.
I think SCMs are not well suited for dependencies management. I prefer having a dedicated tool for that (this would be your solution n°3).
My current project is in Java. It was built with Apache Ant, and I first set up Apache Ivy as a dependencies management tool. In the end, the setup consisted of some Ivy configuration files in a shared directory, and one XML file listing the dependencies for each module of the project. Ivy can be invoked by Ant targets, so I added two new actions in each module : "resolve dependencies", and "deploy the built artifact". The deployment adds the result of the buid (called an artifact) in the shared directory. The dependencies resolution means transitively resolving the dependencies of the module, and copying the resolved artifacts in the "lib" folder of the module's sources.
This solution is applicable to a C++ project, since Ivy is not specific to managing Java dependencies : artifacts can be anything. In C++, the artifacts produced by a module would be :
- a so/dll at runtime
- the header files at compile time.
This is not a perfect solution: Ivy is not easy to set up, you still have to tell your build script what dependencies to use, and you do not have direct access to the sources of the dependencies for debugging purpose. But you do end up with independent SCM repositories.
In our project, we then switched form Ant+Ivy to Apache Maven, which takes care of both the build and the dependencies management. The artifacts are deployed in an Apache Archiva instead of a shared folder. This is a huge improvement, but it will work well for Java projects only.
What you want to do is have each project in its own directory like in (1). Then you tag working versions of your dependencies and save the tag in some file for build like:
App1/.dependencies: CommonLib1 tag-20100515 CommonLib2 tag-20100510 App2/.dependencies: CommonLib1 tag-20100510 CommonLib2 tag-20100510
Then you use your build scripts to build the libraries based on the specific tag and include those built libraries as derived objects for your applications. If build time is an issue, you can have the tagged version that are in use for those libraries pre-built and saved somewhere.
Note (design principles are same if designing database schema, object model or product build):
- Do not link to the code in other projects (breaks encapsulation)
- Do not have multiple copies of the libraries in your repository (modularity)
We solved a similar problem using subversion.
Each app and each Common Lib have their own repositories.
Every app has a directory Libs that contain the dependent dlls.
so the app only gets an update of Common Lib if a new set of dlls is provided.
however upgrading the lib folder is not trivial because depending sub-dlls must match the correct version.
'IT TIP' 카테고리의 다른 글
| 제어 문자의 Socket.IO 문제 (0) | 2020.11.28 |
|---|---|
| kubernetes 서비스 정의에서 targetPort와 포트의 차이점 (0) | 2020.11.28 |
| 데이터 저장소에서 많은 수의 ndb 항목을 쿼리하는 모범 사례 (0) | 2020.11.28 |
| Google에서 특수 문자를 찾고 (0) | 2020.11.28 |
| node.js 멀티 룸 채팅 예제 (0) | 2020.11.28 |