Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/reuse python scm #3576

Merged
merged 8 commits into from Sep 21, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 29 additions & 2 deletions conans/client/cmd/export.py
@@ -1,3 +1,4 @@
import ast
import os
import shutil

Expand All @@ -9,7 +10,7 @@
from conans.model.manifest import FileTreeManifest
from conans.model.scm import SCM
from conans.paths import CONAN_MANIFEST, CONANFILE
from conans.util.files import save, rmdir, is_dirty, set_dirty, mkdir
from conans.util.files import save, rmdir, is_dirty, set_dirty, mkdir, load
from conans.util.log import logger
from conans.search.search import search_recipes

Expand Down Expand Up @@ -89,7 +90,33 @@ def _capture_export_scm_data(conanfile, conanfile_dir, destination_folder, outpu
# Generate the scm_folder.txt file pointing to the src_path
src_path = scm.get_repo_root()
save(scm_src_file, src_path.replace("\\", "/"))
scm_data.replace_in_file(os.path.join(destination_folder, "conanfile.py"))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would feel a bit more comfortable extracting this to a replace_scm function. The overall together is a bit scary.

# Parsing and replacing the SCM field
conanfile_path = os.path.join(destination_folder, "conanfile.py")
content = load(conanfile_path)
lines = content.splitlines(True)
tree = ast.parse(content)
to_replace = []
for item in tree.body:
if isinstance(item, ast.ClassDef):
statements = item.body
for i, stmt in enumerate(item.body):
if isinstance(stmt, ast.Assign) and len(stmt.targets) == 1:
if isinstance(stmt.targets[0], ast.Name) and stmt.targets[0].id == "scm":
try:
next_line = statements[i+1].lineno - 1
except IndexError:
next_line = stmt.lineno
replace = [line for line in lines[(stmt.lineno-1):next_line]
if line.strip()]
to_replace.append("".join(replace).lstrip())
break
if len(to_replace) != 1:
raise ConanException("The conanfile.py defines more than one class level 'scm' attribute")

new_text = "scm = " + ",\n ".join(str(scm_data).split(",")) + "\n"
content = content.replace(to_replace[0], new_text)
save(conanfile_path, content)


def _export_conanfile(conanfile_path, output, paths, conanfile, conan_ref, keep_source):
Expand Down
9 changes: 0 additions & 9 deletions conans/model/scm.py
Expand Up @@ -2,8 +2,6 @@

from conans.client.tools.scm import Git
from conans.errors import ConanException
from conans.util.files import load, save
import re


class SCMData(object):
Expand Down Expand Up @@ -37,13 +35,6 @@ def __repr__(self):
d = {k: v for k, v in d.items() if v}
return json.dumps(d, sort_keys=True)

def replace_in_file(self, path):
content = load(path)
dumps = self.__repr__()
dumps = ",\n ".join(dumps.split(","))
content = re.sub(r"scm\s*=\s*{[^}]*}", "scm = %s" % dumps, content)
save(path, content)


class SCM(object):
def __init__(self, data, repo_folder):
Expand Down
6 changes: 6 additions & 0 deletions conans/test/functional/scm_test.py
Expand Up @@ -123,6 +123,12 @@ def test_auto_git(self):
self.assertIn("Getting sources from folder: %s" % curdir, self.client.out)
self.assertIn("My file is copied", self.client.out)

# check blank lines are respected in replacement
self.client.run("get lib/0.1@user/channel")
self.assertIn("""}

def build(self):""", self.client.out)

# Export again but now with absolute reference, so no pointer file is created nor kept
git = Git(curdir)
self.client.save({"conanfile.py": base.format(url=curdir, revision=git.get_revision())})
Expand Down
44 changes: 43 additions & 1 deletion conans/test/integration/python_build_test.py
@@ -1,5 +1,6 @@
import unittest
from conans.test.utils.tools import TestClient, TestServer
from conans.test.utils.tools import TestClient, TestServer,\
create_local_git_repo
from conans.paths import CONANFILE, BUILD_INFO
from conans.util.files import load, save
import os
Expand Down Expand Up @@ -219,6 +220,47 @@ class PkgTest(base.MyConanfileBase):
self.assertIn("Pkg/0.1@lasote/testing: Package installed 5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9",
client.out)

def reuse_scm_test(self):
client = TestClient()

conanfile = """from conans import ConanFile
scm = {"type" : "git",
"url" : "somerepo",
"revision" : "auto"}

class MyConanfileBase(ConanFile):
scm = scm
"""
create_local_git_repo({"conanfile.py": conanfile}, branch="my_release",
folder=client.current_folder)
client.run("export . MyConanfileBase/1.1@lasote/testing")
client.run("get MyConanfileBase/1.1@lasote/testing")
# The global scm is left as-is
self.assertIn("""scm = {"type" : "git",
"url" : "somerepo",
"revision" : "auto"}""", client.out)
# but the class one is replaced
self.assertNotIn("scm = scm", client.out)
self.assertIn(' scm = {"revision":', client.out)
self.assertIn('"type": "git",', client.out)
self.assertIn('"url": "somerepo"', client.out)

reuse = """from conans import python_requires
base = python_requires("MyConanfileBase/1.1@lasote/testing")
class PkgTest(base.MyConanfileBase):
scm = base.scm
other = 123
def _my_method(self):
pass
"""
client.save({"conanfile.py": reuse})
client.run("export . Pkg/0.1@lasote/testing")
client.run("get Pkg/0.1@lasote/testing")
self.assertNotIn("scm = base.scm", client.out)
self.assertIn('scm = {"revision":', client.out)
self.assertIn('"type": "git",', client.out)
self.assertIn('"url": "somerepo"', client.out)


class PythonBuildTest(unittest.TestCase):

Expand Down