How to capture package version from SCM: git

The Git() helper from tools, can be used to capture data from the git repo where the conanfile.py recipe lives, and use it to define the version of the conan package.

from conans import ConanFile, tools

def get_version():
    git = tools.Git()
    try:
        return "%s_%s" % (git.get_branch(), git.get_revision())
    except:
        return None

class HelloConan(ConanFile):
    name = "Hello"
    version = get_version()

    def build(self):
        ...

In this example, the package created with conan create will be called Hello/branch_commit@user/channel. Note that the get_version() returns None if it is not able to get the git data. This is necessary, when the recipe is already in the conan cache, and the git repository might not be there, a None value makes conan get the version from the metadata.

How to capture package version from text or build files

It is common that a library version number would be already encoded in a text file, in some build scripts, etc. Lets take as an example that we have the following library layout, that we want to create a package from it:

conanfile.py
CMakeLists.txt
src
   hello.cpp
   ...

The CMakeLists.txt will have some variables to define the library version number. Lets assume for simplicity that it has some line like:

cmake_minimum_required(VERSION 2.8)
set(MY_LIBRARY_VERSION 1.2.3) # This is the version we want
add_library(hello src/hello.cpp)

We will typically have in our conanfile.py package recipe:

class HelloConan(ConanFile):
    name = "Hello"
    version = "1.2.3"

Usually this takes very little maintenance, and when the CMakeLists version is bumped, the conanfile.py version is bumped too. But if you want to only have to update the CMakeLists.txt version, you can extract the version dynamically, with:

from conans import ConanFile
from conans.tools import load
import re

def get_version():
    try:
        content = load("CMakeLists.txt")
        version = re.search(b"set\(MY_LIBRARY_VERSION (.*)\)", content).group(1)
        return version.strip()
    except Exception as e:
        return None

class HelloConan(ConanFile):
    name = "Hello"
    version = get_version()

Even if the CMakeLists.txt file is not exported to the local cache, it will still work, as the get_version() function returns None when it is not found, then taking the version number from the package metadata (layout).