summaryrefslogtreecommitdiffstatshomepage
path: root/cgit.mk
blob: 3fcc1ca3144038859b57a88446e7af9b2abf9260 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# This Makefile is run in the "git" directory in order to re-use Git's
# build variables and operating system detection.  Hence all files in
# CGit's directory must be prefixed with "../".
include Makefile

CGIT_PREFIX = ../

-include $(CGIT_PREFIX)cgit.conf

# The CGIT_* variables are inherited when this file is called from the
# main Makefile - they are defined there.

$(CGIT_PREFIX)VERSION: force-version
	@cd $(CGIT_PREFIX) && '$(SHELL_PATH_SQ)' ./gen-version.sh "$(CGIT_VERSION)"
-include $(CGIT_PREFIX)VERSION
.PHONY: force-version

# CGIT_CFLAGS is a separate variable so that we can track it separately
# and avoid rebuilding all of Git when these variables change.
CGIT_CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
CGIT_CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
CGIT_CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'

PKG_CONFIG ?= pkg-config

ifdef NO_C99_FORMAT
	CFLAGS += -DNO_C99_FORMAT
endif

ifdef NO_LUA
	LUA_MESSAGE := linking without specified Lua support
	CGIT_CFLAGS += -DNO_LUA
else
ifeq ($(LUA_PKGCONFIG),)
	LUA_PKGCONFIG := $(shell for pc in luajit lua lua5.2 lua5.1; do \
			$(PKG_CONFIG) --exists $$pc 2>/dev/null && echo $$pc && break; \
			done)
	LUA_MODE := autodetected
else
	LUA_MODE := specified
endif
ifneq ($(LUA_PKGCONFIG),)
	LUA_MESSAGE := linking with $(LUA_MODE) $(LUA_PKGCONFIG)
	LUA_LIBS := $(shell $(PKG_CONFIG) --libs $(LUA_PKGCONFIG) 2>/dev/null)
	LUA_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(LUA_PKGCONFIG) 2>/dev/null)
	CGIT_LIBS += $(LUA_LIBS)
	CGIT_CFLAGS += $(LUA_CFLAGS)
else
	LUA_MESSAGE := linking without autodetected Lua support
	NO_LUA := YesPlease
	CGIT_CFLAGS += -DNO_LUA
endif

endif

# Add -ldl to linker flags on systems that commonly use GNU libc.
ifneq (,$(filter $(uname_S),Linux GNU GNU/kFreeBSD))
	CGIT_LIBS += -ldl
endif

# glibc 2.1+ offers sendfile which the most common C library on Linux
ifeq ($(uname_S),Linux)
	HAVE_LINUX_SENDFILE = YesPlease
endif

ifdef HAVE_LINUX_SENDFILE
	CGIT_CFLAGS += -DHAVE_LINUX_SENDFILE
endif

CGIT_OBJ_NAMES += cgit.o
CGIT_OBJ_NAMES += cache.o
CGIT_OBJ_NAMES += cmd.o
CGIT_OBJ_NAMES += configfile.o
CGIT_OBJ_NAMES += filter.o
CGIT_OBJ_NAMES += html.o
CGIT_OBJ_NAMES += parsing.o
CGIT_OBJ_NAMES += scan-tree.o
CGIT_OBJ_NAMES += shared.o
CGIT_OBJ_NAMES += ui-atom.o
CGIT_OBJ_NAMES += ui-blame.o
CGIT_OBJ_NAMES += ui-blob.o
CGIT_OBJ_NAMES += ui-clone.o
CGIT_OBJ_NAMES += ui-commit.o
CGIT_OBJ_NAMES += ui-diff.o
CGIT_OBJ_NAMES += ui-log.o
CGIT_OBJ_NAMES += ui-patch.o
CGIT_OBJ_NAMES += ui-plain.o
CGIT_OBJ_NAMES += ui-refs.o
CGIT_OBJ_NAMES += ui-repolist.o
CGIT_OBJ_NAMES += ui-shared.o
CGIT_OBJ_NAMES += ui-snapshot.o
CGIT_OBJ_NAMES += ui-ssdiff.o
CGIT_OBJ_NAMES += ui-stats.o
CGIT_OBJ_NAMES += ui-summary.o
CGIT_OBJ_NAMES += ui-tag.o
CGIT_OBJ_NAMES += ui-tree.o

CGIT_OBJS := $(addprefix $(CGIT_PREFIX),$(CGIT_OBJ_NAMES))

# Only cgit.c reference CGIT_VERSION so we only rebuild its objects when the
# version changes.
CGIT_VERSION_OBJS := $(addprefix $(CGIT_PREFIX),cgit.o cgit.sp)
$(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION
$(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \
	-DCGIT_VERSION='"$(CGIT_VERSION)"'

# Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not
# handled by that and we must handle them ourselves.
cgit_dep_files := $(foreach f,$(CGIT_OBJS),$(dir $f).depend/$(notdir $f).d)
cgit_dep_files_present := $(wildcard $(cgit_dep_files))
ifneq ($(cgit_dep_files_present),)
include $(cgit_dep_files_present)
endif

ifeq ($(wildcard $(CGIT_PREFIX).depend),)
missing_dep_dirs += $(CGIT_PREFIX).depend
endif

$(CGIT_PREFIX).depend:
	@mkdir -p $@

$(CGIT_PREFIX)CGIT-CFLAGS: FORCE
	@FLAGS='$(subst ','\'',$(CGIT_CFLAGS))'; \
	    if test x"$$FLAGS" != x"`cat ../CGIT-CFLAGS 2>/dev/null`" ; then \
		echo 1>&2 "    * new CGit build flags"; \
		echo "$$FLAGS" >$(CGIT_PREFIX)CGIT-CFLAGS; \
            fi

$(CGIT_OBJS): %.o: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS $(missing_dep_dirs)
	$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $<

$(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS)
	@echo 1>&1 "    * $(LUA_MESSAGE)"
	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(CGIT_LIBS)

CGIT_SP_OBJS := $(patsubst %.o,%.sp,$(CGIT_OBJS))

$(CGIT_SP_OBJS): %.sp: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS FORCE
	$(QUIET_SP)cgcc -no-compile $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $(SPARSE_FLAGS) $<

cgit-sparse: $(CGIT_SP_OBJS)
>unsigned long ofs, idx; static char ascii[ROWLEN + 1]; html("<table summary='blob content' class='bin-blob'>\n"); html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { htmlf("<tr><td class='right'>%04lx</td><td class='hex'>", ofs); for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) htmlf("%*s%02x", idx == 16 ? 4 : 1, "", buf[idx] & 0xff); html(" </td><td class='hex'>"); for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; ascii[idx] = '\0'; html_txt(ascii); html("</td></tr>\n"); } html("</table>\n"); } static void print_object(const struct object_id *oid, const char *path, const char *basename, const char *rev) { enum object_type type; char *buf; unsigned long size; type = oid_object_info(the_repository, oid, &size); if (type == OBJ_BAD) { cgit_print_error_page(404, "Not found", "Bad object name: %s", oid_to_hex(oid)); return; } buf = read_object_file(oid, &type, &size); if (!buf) { cgit_print_error_page(500, "Internal server error", "Error reading object %s", oid_to_hex(oid)); return; } cgit_set_title_from_path(path); cgit_print_layout_start(); htmlf("blob: %s (", oid_to_hex(oid)); cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path); if (ctx.repo->enable_blame) { html(") ("); cgit_blame_link("blame", NULL, NULL, ctx.qry.head, rev, path); } html(")\n"); if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) { htmlf("<div class='error'>blob size (%ldKB) exceeds display size limit (%dKB).</div>", size / 1024, ctx.cfg.max_blob_size); return; } if (buffer_is_binary(buf, size)) print_binary_buffer(buf, size); else print_text_buffer(basename, buf, size); free(buf); } struct single_tree_ctx { struct strbuf *path; struct object_id oid; char *name; size_t count; }; static int single_tree_cb(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *cbdata) { struct single_tree_ctx *ctx = cbdata; if (++ctx->count > 1) return -1; if (!S_ISDIR(mode)) { ctx->count = 2; return -1; } ctx->name = xstrdup(pathname); oidcpy(&ctx->oid, oid); strbuf_addf(ctx->path, "/%s", pathname); return 0; } static void write_tree_link(const struct object_id *oid, char *name, char *rev, struct strbuf *fullpath) { size_t initial_length = fullpath->len; struct tree *tree; struct single_tree_ctx tree_ctx = { .path = fullpath, .count = 1, }; struct pathspec paths = { .nr = 0 }; oidcpy(&tree_ctx.oid, oid); while (tree_ctx.count == 1) { cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, rev, fullpath->buf); tree = lookup_tree(the_repository, &tree_ctx.oid); if (!tree) return; free(tree_ctx.name); tree_ctx.name = NULL; tree_ctx.count = 0; read_tree_recursive(the_repository, tree, "", 0, 1, &paths, single_tree_cb, &tree_ctx); if (tree_ctx.count != 1) break; html(" / "); name = tree_ctx.name; } strbuf_setlen(fullpath, initial_length); } static int ls_item(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *cbdata) { struct walk_tree_context *walk_tree_ctx = cbdata; char *name; struct strbuf fullpath = STRBUF_INIT; struct strbuf class = STRBUF_INIT; enum object_type type; unsigned long size = 0; name = xstrdup(pathname); strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "", ctx.qry.path ? "/" : "", name); if (!S_ISGITLINK(mode)) { type = oid_object_info(the_repository, oid, &size); if (type == OBJ_BAD) { htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", name, oid_to_hex(oid)); free(name); return 0; } } html("<tr><td class='ls-mode'>"); cgit_print_filemode(mode); html("</td><td>"); if (S_ISGITLINK(mode)) { cgit_submodule_link("ls-mod", fullpath.buf, oid_to_hex(oid)); } else if (S_ISDIR(mode)) { write_tree_link(oid, name, walk_tree_ctx->curr_rev, &fullpath); } else { char *ext = strrchr(name, '.'); strbuf_addstr(&class, "ls-blob"); if (ext) strbuf_addf(&class, " %s", ext + 1); cgit_tree_link(name, NULL, class.buf, ctx.qry.head, walk_tree_ctx->curr_rev, fullpath.buf); } htmlf("</td><td class='ls-size'>%li</td>", size); html("<td>"); cgit_log_link("log", NULL, "button", ctx.qry.head, walk_tree_ctx->curr_rev, fullpath.buf, 0, NULL, NULL, ctx.qry.showmsg, 0); if (ctx.repo->max_stats) cgit_stats_link("stats", NULL, "button", ctx.qry.head, fullpath.buf); if (!S_ISGITLINK(mode)) cgit_plain_link("plain", NULL, "button", ctx.qry.head, walk_tree_ctx->curr_rev, fullpath.buf); if (!S_ISDIR(mode) && ctx.repo->enable_blame) cgit_blame_link("blame", NULL, "button", ctx.qry.head, walk_tree_ctx->curr_rev, fullpath.buf); html("</td></tr>\n"); free(name); strbuf_release(&fullpath); strbuf_release(&class); return 0; } static void ls_head(void) { cgit_print_layout_start(); html("<table summary='tree listing' class='list'>\n"); html("<tr class='nohover'>"); html("<th class='left'>Mode</th>"); html("<th class='left'>Name</th>"); html("<th class='right'>Size</th>"); html("<th/>"); html("</tr>\n"); } static void ls_tail(void) { html("</table>\n"); cgit_print_layout_end(); } static void ls_tree(const struct object_id *oid, const char *path, struct walk_tree_context *walk_tree_ctx) { struct tree *tree; struct pathspec paths = { .nr = 0 }; tree = parse_tree_indirect(oid); if (!tree) { cgit_print_error_page(404, "Not found", "Not a tree object: %s", oid_to_hex(oid)); return; } ls_head(); read_tree_recursive(the_repository, tree, "", 0, 1, &paths, ls_item, walk_tree_ctx); ls_tail(); } static int walk_tree(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *cbdata) { struct walk_tree_context *walk_tree_ctx = cbdata; if (walk_tree_ctx->state == 0) { struct strbuf buffer = STRBUF_INIT; strbuf_addbuf(&buffer, base); strbuf_addstr(&buffer, pathname); if (strcmp(walk_tree_ctx->match_path, buffer.buf)) return READ_TREE_RECURSIVE; if (S_ISDIR(mode)) { walk_tree_ctx->state = 1; cgit_set_title_from_path(buffer.buf); strbuf_release(&buffer); ls_head(); return READ_TREE_RECURSIVE; } else { walk_tree_ctx->state = 2; print_object(oid, buffer.buf, pathname, walk_tree_ctx->curr_rev); strbuf_release(&buffer); return 0; } } ls_item(oid, base, pathname, mode, stage, walk_tree_ctx); return 0; } /* * Show a tree or a blob * rev: the commit pointing at the root tree object * path: path to tree or blob */ void cgit_print_tree(const char *rev, char *path) { struct object_id oid; struct commit *commit; struct pathspec_item path_items = { .match = path, .len = path ? strlen(path) : 0 }; struct pathspec paths = { .nr = path ? 1 : 0, .items = &path_items }; struct walk_tree_context walk_tree_ctx = { .match_path = path, .state = 0 }; if (!rev) rev = ctx.qry.head; if (get_oid(rev, &oid)) { cgit_print_error_page(404, "Not found", "Invalid revision name: %s", rev); return; } commit = lookup_commit_reference(the_repository, &oid); if (!commit || parse_commit(commit)) { cgit_print_error_page(404, "Not found", "Invalid commit reference: %s", rev); return; } walk_tree_ctx.curr_rev = xstrdup(rev); if (path == NULL) { ls_tree(get_commit_tree_oid(commit), NULL, &walk_tree_ctx); goto cleanup; } read_tree_recursive(the_repository, repo_get_commit_tree(the_repository, commit), "", 0, 0, &paths, walk_tree, &walk_tree_ctx); if (walk_tree_ctx.state == 1) ls_tail(); else if (walk_tree_ctx.state == 2) cgit_print_layout_end(); else cgit_print_error_page(404, "Not found", "Path not found"); cleanup: free(walk_tree_ctx.curr_rev); }