summaryrefslogtreecommitdiffstats
path: root/solenv/clang-format/ClangFormat.pm
blob: 30f3816231bf855556d37eaf08c1913898d64487 (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
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

package ClangFormat;

use strict;
use warnings;

our @EXPORT_OK = qw(get_excludelist set_excludelist get_wanted_version get_own_directory get_extension_regex find check_style);

# Reads the excludelist.
sub get_excludelist()
{
    my $src = "c|cpp|cxx|h|hxx|inl";
    my %excludelist_names = ();

    # Read the excludelist.
    if (open(LINES, "solenv/clang-format/excludelist"))
    {
        while (my $line = <LINES>)
        {
            chomp $line;
            $excludelist_names{$line} = 1;
        }
    }

    return \%excludelist_names;
}

# Writes the excludelist.
# The single argument is a reference to an array.
sub set_excludelist
{
    my @filenames = @{$_[0]};
    open my $fh, ">", "solenv/clang-format/excludelist" or die $!;
    print $fh "$_\n" for @filenames;
    close $fh;
}

# Returns the clang-format version used of style enforcement.
sub get_wanted_version()
{
    return "5.0.0";
}

# Returns the directory that can host a binary which is used automatically, even
# if it's not in PATH.
sub get_own_directory()
{
    return "/opt/lo/bin";
}

# Returns a regex matching filenames we clang-format.
sub get_extension_regex()
{
    return "c|cpp|cxx|h|hxx|inl";
}

# Use clang-format from CLANG_FORMAT, from our dedicated directory or from
# PATH, in this order.
sub find()
{
    my $version = get_wanted_version();
    my $opt_lo = get_own_directory();
    my $clang_format;
    if (!(defined($ENV{CLANG_FORMAT}) && is_matching_clang_format_version($ENV{CLANG_FORMAT}, $version)))
    {
        my @dirs = split /:/, $ENV{PATH};
        unshift(@dirs, $opt_lo);

        foreach my $dir (@dirs)
        {
            if (is_matching_clang_format_version("$dir/clang-format", $version))
            {
                $clang_format = "$dir/clang-format";
                last;
            }
        }
    }
    else
    {
        $clang_format = $ENV{CLANG_FORMAT};
    }

    if ($^O eq "cygwin" && defined($clang_format))
    {
        $clang_format = `cygpath -m '$clang_format'`;
        chomp $clang_format;
    }

    return $clang_format;
}

# Diffs the original and the formatted version of a single file from the index.
sub check_style($$)
{
    # Make sure that not staged changes are not considered when diffing.
    my ($clang_format, $filename) = @_;
    my $index = $filename . ".index";
    system("git show :$filename > $index");
    my $format = $index . ".format";
    system("'$clang_format' -assume-filename=$filename $index > $format");
    my $ret = system("git --no-pager diff --no-index --exit-code $index $format") == 0;
    unlink($index);
    unlink($format);
    return $ret;
}

# Private functions.

# Is this binary the version we standardize on?
sub is_matching_clang_format_version($$)
{
    my ($clang_format, $version) = @_;
    if (! -x $clang_format)
    {
        return 0;
    }

    return `'$clang_format' -version` =~ /^clang-format version $version(-\d+)? \(tags/;
}

1;

# vim: set shiftwidth=4 softtabstop=4 expandtab: